Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> blindScope, merging); } private FlowScope caseEquality(Node left, Node right, FlowScope blindScope, Function<TypePair, TypePair> merging) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftType = left.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, condition); } // restricting left type JSType restrictedLeftType = (leftType == null) ? null : leftType.getRestrictedTypeGivenToBooleanOutcome(condition); if (restrictedLeftType == null) { return firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); left, blindScope, condition); rightScope = firstPreciserScopeKnowingConditionOutcome( right, rightScope, !condition); StaticSlot<JSType> rightVar = rightScope.findUniqueRefinedSlot(blindScope); if (rightVar == null || !leftVar.getName().equals(rightVar.getName())) { return blindScope; } JSType type = leftVar.getType().getLeastSupertype(rightVar.getType()); FlowScope informed = blindScope.createChildFlowScope(); informed.inferSlotType(leftVar.getName(), type); return informed; } if (leftType == null) { return blindScope; } JSType rightType = right.getJSType(); ObjectType targetType = typeRegistry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE); if (rightType != null && rightType.isFunctionType()) { targetType = rightType.toMaybeFunctionType(); } Visitor<JSType> visitor; if (outcome) { visitor = new RestrictByTrueInstanceOfResultVisitor(targetType); } else { visitor = new RestrictByFalseInstanceOfResultVisitor(targetType); } return maybeRestrictName( blindScope, left, leftType, leftType.visit(visitor)); } /** * Given 'property in object', ensures that the object has the property in the * informed scope by defining it as a qualified name

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> if the object type lacks * the property and it's not in the blind scope. * @param object The node of the right-side of the in. * @param propertyName The string of the left-side of the in. */ private FlowScope caseIn(Node object, String propertyName, FlowScope blindScope) { JSType jsType = object.getJSType(); jsType = this.getRestrictedWithoutNull(jsType); jsType = this.getRestrictedWithoutUndefined(jsType); boolean hasProperty = false; ObjectType objectType = ObjectType.cast(jsType); if (objectType != null) { hasProperty = objectType.hasProperty(propertyName); } if (!hasProperty) { String qualifiedName = object.getQualifiedName(); if (qualifiedName != null) { String propertyQualifiedName = qualifiedName + "." + propertyName; if (blindScope.getSlot(propertyQualifiedName) == null) { FlowScope informed = blindScope.createChildFlowScope(); JSType unknownType = typeRegistry.getNativeType( JSTypeNative.UNKNOWN_TYPE); informed.inferQualifiedSlot( object, propertyQualifiedName, unknownType, unknownType); return informed; } } } return blindScope; } /** * @see SemanticReverseAbstractInterpreter#caseInstanceOf */ private class RestrictByTrueInstanceOfResultVisitor extends RestrictByTrueTypeOfResultVisitor { private final ObjectType target; RestrictByTrueInstanceOfResultVisitor(ObjectType target) { this.target = target; } @Override protected JSType caseTopType(JSType type) { return applyCommonRestriction(type); } @Override public JSType caseUnknownType() { FunctionType funcTarget = JSType.toMaybeFunctionType(target); if (funcTarget != null && funcTarget.hasInstanceType()) { return funcTarget.getInstanceType(); } return getNativeType(UNKNOWN_TYPE); } @Override public JSType caseObjectType(ObjectType type) { return applyCommonRestriction(type); } @Override public JSType caseUnionType(UnionType type) { return applyCommonRestriction(type); } @Override public JSType caseFunctionType(FunctionType type) { return caseObjectType(type);

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> &#8743; Object} = {@code Any}</li> * <li>{@code Number &#8743; Object} = {@code Number}</li> * </ul> * @return {@code this &#8744; that} */ public JSType getGreatestSubtype(JSType that) { return getGreatestSubtype(this, that); } /** * A generic implementation meant to be used as a helper for common * getGreatestSubtype implementations. */ static JSType getGreatestSubtype(JSType thisType, JSType thatType) { if (thisType.isFunctionType() && thatType.isFunctionType()) { // The FunctionType sub-lattice is not well-defined. i.e., the // proposition // A < B => sup(A, B) == B // does not hold because of unknown parameters and return types. // See the comment in supAndInfHelper for more info on this. return thisType.toMaybeFunctionType().supAndInfHelper( thatType.toMaybeFunctionType(), false); } else if (thisType.isEquivalentTo(thatType)) { return thisType; } else if (thisType.isUnknownType() || thatType.isUnknownType()) { // The greatest subtype with any unknown type is the universal // unknown type, unless the two types are equal. return thisType.isEquivalentTo(thatType) ? thisType : thisType.getNativeType(JSTypeNative.UNKNOWN_TYPE); } else if (thisType.isSubtype(thatType)) { return filterNoResolvedType(thisType); } else if (thatType.isSubtype(thisType)) { return filterNoResolvedType(thatType); } else if (thisType.isUnionType()) { return thisType.toMaybeUnionType().meet(thatType); } else if (thatType.isUnionType()) { return thatType.toMaybeUnionType().meet(thisType); } else if (thisType.isRecordType()) { return thisType.toMaybeRecordType().getGreatestSubtypeHelper(thatType); } else if (thatType.isRecordType()) { return thatType.toMaybeRecordType().getGreatestSubtypeHelper(thisType);

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> T visit(Visitor<T> visitor); /** * Force this type to resolve, even if the registry is in a lazy * resolving mode. * @see #resolve */ public final JSType forceResolve(ErrorReporter t, StaticScope<JSType> scope) { ResolveMode oldResolveMode = registry.getResolveMode(); registry.setResolveMode(ResolveMode.IMMEDIATE); JSType result = resolve(t, scope); registry.setResolveMode(oldResolveMode); return result; } /** * Resolve this type in the given scope. * * The returned value must be equal to {@code this}, as defined by * {@link #isEquivalentTo}. It may or may not be the same object. This method * may modify the internal state of {@code this}, as long as it does * so in a way that preserves Object equality. * * For efficiency, we should only resolve a type once per compilation job. * For incremental compilations, one compilation job may need the * artifacts from a previous generation, so we will eventually need * a generational flag instead of a boolean one. */ public final JSType resolve(ErrorReporter t, StaticScope<JSType> scope) { if (resolved) { // TODO(nicksantos): Check to see if resolve() looped back on itself. // Preconditions.checkNotNull(resolveResult); if (resolveResult == null) { return registry.getNativeType(JSTypeNative.UNKNOWN_TYPE); } return resolveResult; } resolved = true; resolveResult = resolveInternal(t, scope); resolveResult.setResolvedTypeInternal(resolveResult); return resolveResult; } /** * @see #resolve */ abstract JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope); void setResolvedTypeInternal(JSType type) { resolveResult = type; resolved = true; } /** Whether the type has been resolved. */ public final boolean isResolved() { return resolved; } /** Clears the resolved field. */ public final void clearResolved() { resolved = false; resolveResult = null; } /** * A null-safe resolve. * @see #resolve */ static final JS

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>.isObjectLitKey(n, parent)) { JSDocInfo info = n.getJSDocInfo(); if (info == null) { info = parent.getJSDocInfo(); } allowDupe = info != null && info.getSuppressions().contains("duplicate"); } JSType varType = var.getType(); // Only report duplicate declarations that have types. Other duplicates // will be reported by the syntactic scope creator later in the // compilation process. if (varType != null && varType != typeRegistry.getNativeType(UNKNOWN_TYPE) && newType != null && newType != typeRegistry.getNativeType(UNKNOWN_TYPE)) { // If there are two typed declarations of the same variable, that // is an error and the second declaration is ignored, except in the // case of native types. A null input type means that the declaration // was made in TypedScopeCreator#createInitialScope and is a // native type. We should redeclare it at the new input site. if (var.input == null) { Scope s = var.getScope(); s.undeclare(var); newVar = s.declare(variableName, n, varType, input, false); n.setJSType(varType); if (parent.isVar()) { if (n.getFirstChild() != null) { n.getFirstChild().setJSType(varType); } } else { Preconditions.checkState(parent.isFunction()); parent.setJSType(varType); } } else { // Always warn about duplicates if the overridden type does not // match the original type. // // If the types match, suppress the warning iff there was a @suppress // tag, or if the original declaration was a stub. if (!(allowDupe || var.getParentNode().isExprResult()) || !newType.equals(varType)) { report(JSError.make(sourceName, n, DUP_VAR_DECLARATION, variableName, newType.toString(), var.getInputName(), String.valueOf(var.nameNode.getLineno()), varType.toString())); } } } return newVar; } /** * Expect that all properties

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> This method gets the JSType from the Node argument and verifies that it is * present. */ private JSType getJSType(Node n) { JSType jsType = n.getJSType(); if (jsType == null) { // TODO(user): This branch indicates a compiler bug, not worthy of // halting the compilation but we should log this and analyze to track // down why it happens. This is not critical and will be resolved over // time as the type checker is extended. return getNativeType(UNKNOWN_TYPE); } else { return jsType; } } private JSType getNativeType(JSTypeNative typeId) { return typeRegistry.getNativeType(typeId); } private JSError report(JSError error) { if (shouldReport) { compiler.report(error); } return error; } /** * Signals that the first type and the second type have been * used interchangeably. * * Type-based optimizations should take this into account * so that they don't wreck code with type warnings. */ static class TypeMismatch { final JSType typeA; final JSType typeB; final JSError src; /** * It's the responsibility of the class that creates the * {@code TypeMismatch} to ensure that {@code a} and {@code b} are * non-matching types. */ TypeMismatch(JSType a, JSType b, JSError src) { this.typeA = a; this.typeB = b; this.src = src; } @Override public boolean equals(Object object) { if (object instanceof TypeMismatch) { TypeMismatch that = (TypeMismatch) object; return (that.typeA.equals(this.typeA) && that.typeB.equals(this.typeB)) || (that.typeB.equals(this.typeA) && that.typeA.equals(this.typeB)); } return false; } @Override public int hashCode() { return Objects.hashCode(typeA, typeB); } @Override public String toString() { return "(" + typeA + ", " + typeB + ")"; } } }

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE)); this.name = name; } @Override public String getReferenceName() { return name; } @Override String toStringHelper(boolean forAnnotations) { return name; } @Override public TemplateType toMaybeTemplateType() { return this; } @Override public boolean hasAnyTemplateInternal() { return true; } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseTemplateType(this); } }

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> = getNativeType(JSTypeNative.UNKNOWN_TYPE); name.setJSType(type); redeclareSimpleVar(scope, name, type); return scope; } private FlowScope traverseAssign(Node n, FlowScope scope) { Node left = n.getFirstChild(); Node right = n.getLastChild(); scope = traverseChildren(n, scope); JSType leftType = left.getJSType(); JSType rightType = getJSType(right); n.setJSType(rightType); updateScopeForTypeChange(scope, left, leftType, rightType); return scope; } /** * Updates the scope according to the result of a type change, like * an assignment or a type cast. */ private void updateScopeForTypeChange( FlowScope scope, Node left, JSType leftType, JSType resultType) { Preconditions.checkNotNull(resultType); switch (left.getType()) { case Token.NAME: String varName = left.getString(); Var var = syntacticScope.getVar(varName); // When looking at VAR initializers for declared VARs, we trust // the declared type over the type it's being initialized to. // This has two purposes: // 1) We avoid re-declaring declared variables so that built-in // types defined in externs are not redeclared. // 2) When there's a lexical closure like // /** @type {?string} */ var x = null; // function f() { x = 'xyz'; } // the inference will ignore the lexical closure, // which is just wrong. This bug needs to be fixed eventually. boolean isVarDeclaration = left.hasChildren(); if (!isVarDeclaration || var == null || var.isTypeInferred()) { redeclareSimpleVar(scope, left, resultType); } left.setJSType(isVarDeclaration || leftType == null ? resultType : null); if (var != null && var.isTypeInferred()) { JSType oldType = var.getType(); var.setType(oldType == null ? resultType : oldType.getLeastSupertype(resultType)); } break; case Token.GETPROP: String qualifiedName = left.

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>getQualifiedName(); if (qualifiedName != null) { scope.inferQualifiedSlot(left, qualifiedName, leftType == null ? getNativeType(UNKNOWN_TYPE) : leftType, resultType); } left.setJSType(resultType); ensurePropertyDefined(left, resultType); break; } } /** * Defines a property if the property has not been defined yet. */ private void ensurePropertyDefined(Node getprop, JSType rightType) { String propName = getprop.getLastChild().getString(); JSType nodeType = getJSType(getprop.getFirstChild()); ObjectType objectType = ObjectType.cast( nodeType.restrictByNotNullOrUndefined()); if (objectType == null) { registry.registerPropertyOnType(propName, nodeType); } else { if (ensurePropertyDeclaredHelper(getprop, objectType)) { return; } if (!objectType.isPropertyTypeDeclared(propName)) { // We do not want a "stray" assign to define an inferred property // for every object of this type in the program. So we use a heuristic // approach to determine whether to infer the property. // // 1) If the property is already defined, join it with the previously // inferred type. // 2) If this isn't an instance object, define it. // 3) If the property of an object is being assigned in the constructor, // define it. // 4) If this is a stub, define it. // 5) Otherwise, do not define the type, but declare it in the registry // so that we can use it for missing property checks. if (objectType.hasProperty(propName) || !objectType.isInstanceType()) { if ("prototype".equals(propName)) { objectType.defineDeclaredProperty( propName, rightType, getprop); } else { objectType.defineInferredProperty( propName, rightType, getprop); } } else { if (getprop.getFirstChild().isThis() && getJSType(syntacticScope.getRootNode()).isConstructor()) { objectType.defineInferredProperty( propName, rightType, getprop); } else

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> we have it. // 1) The var is escaped in a weird way, e.g., // function f() { var x = 3; function g() { x = null } (x); } boolean isInferred = var.isTypeInferred(); boolean unflowable = isInferred && isUnflowable(syntacticScope.getVar(varName)); // 2) We're reading type information from another scope for an // inferred variable. // var t = null; function f() { (t); } boolean nonLocalInferredSlot = isInferred && syntacticScope.getParent() != null && var == syntacticScope.getParent().getSlot(varName); if (!unflowable && !nonLocalInferredSlot) { type = var.getType(); if (type == null) { type = getNativeType(UNKNOWN_TYPE); } } } } n.setJSType(type); return scope; } /** Traverse each element of the array. */ private FlowScope traverseArrayLiteral(Node n, FlowScope scope) { scope = traverseChildren(n, scope); n.setJSType(getNativeType(ARRAY_TYPE)); return scope; } private FlowScope traverseObjectLiteral(Node n, FlowScope scope) { JSType type = n.getJSType(); Preconditions.checkNotNull(type); for (Node name = n.getFirstChild(); name != null; name = name.getNext()) { scope = traverse(name.getFirstChild(), scope); } // Object literals can be reflected on other types, or changed with // type casts. // See CodingConvention#getObjectLiteralCase and goog.object.reflect. // Ignore these types of literals. // TODO(nicksantos): There should be an "anonymous object" type that // we can check for here. ObjectType objectType = ObjectType.cast(type); if (objectType == null) { return scope; } boolean hasLendsName = n.getJSDocInfo() != null && n.getJSDocInfo().getLendsName() != null; if (objectType.hasReferenceName() && !hasLendsName) { return scope; } String qObjName = NodeUtil.

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>getBestLValueName( NodeUtil.getBestLValue(n)); for (Node name = n.getFirstChild(); name != null; name = name.getNext()) { Node value = name.getFirstChild(); String memberName = NodeUtil.getObjectLitKeyName(name); if (memberName != null) { JSType rawValueType = name.getFirstChild().getJSType(); JSType valueType = NodeUtil.getObjectLitKeyTypeFromValueType( name, rawValueType); if (valueType == null) { valueType = getNativeType(UNKNOWN_TYPE); } objectType.defineInferredProperty(memberName, valueType, name); // Do normal flow inference if this is a direct property assignment. if (qObjName != null && name.isStringKey()) { String qKeyName = qObjName + "." + memberName; Var var = syntacticScope.getVar(qKeyName); JSType oldType = var == null ? null : var.getType(); if (var != null && var.isTypeInferred()) { var.setType(oldType == null ? valueType : oldType.getLeastSupertype(oldType)); } scope.inferQualifiedSlot(name, qKeyName, oldType == null ? getNativeType(UNKNOWN_TYPE) : oldType, valueType); } } else { n.setJSType(getNativeType(UNKNOWN_TYPE)); } } return scope; } private FlowScope traverseAdd(Node n, FlowScope scope) { Node left = n.getFirstChild(); Node right = left.getNext(); scope = traverseChildren(n, scope); JSType leftType = left.getJSType(); JSType rightType = right.getJSType(); JSType type = getNativeType(UNKNOWN_TYPE); if (leftType != null && rightType != null) { boolean leftIsUnknown = leftType.isUnknownType(); boolean rightIsUnknown = rightType.isUnknownType(); if (leftIsUnknown && rightIsUnknown) { type = getNativeType(UNKNOWN_TYPE); } else if ((!leftIsUnknown && leftType.isString()) || (!rightIsUnknown && rightType.isString())) { type = getNativeType(STRING_TYPE); }

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> else if (leftIsUnknown || rightIsUnknown) { type = getNativeType(UNKNOWN_TYPE); } else if (isAddedAsNumber(leftType) && isAddedAsNumber(rightType)) { type = getNativeType(NUMBER_TYPE); } else { type = registry.createUnionType(STRING_TYPE, NUMBER_TYPE); } } n.setJSType(type); if (n.isAssignAdd()) { updateScopeForTypeChange(scope, left, leftType, type); } return scope; } private boolean isAddedAsNumber(JSType type) { return type.isSubtype(registry.createUnionType(VOID_TYPE, NULL_TYPE, NUMBER_VALUE_OR_OBJECT_TYPE, BOOLEAN_TYPE, BOOLEAN_OBJECT_TYPE)); } private FlowScope traverseHook(Node n, FlowScope scope) { Node condition = n.getFirstChild(); Node trueNode = condition.getNext(); Node falseNode = n.getLastChild(); // verify the condition scope = traverse(condition, scope); // reverse abstract interpret the condition to produce two new scopes FlowScope trueScope = reverseInterpreter. getPreciserScopeKnowingConditionOutcome( condition, scope, true); FlowScope falseScope = reverseInterpreter. getPreciserScopeKnowingConditionOutcome( condition, scope, false); // traverse the true node with the trueScope traverse(trueNode, trueScope.createChildFlowScope()); // traverse the false node with the falseScope traverse(falseNode, falseScope.createChildFlowScope()); // meet true and false nodes' types and assign JSType trueType = trueNode.getJSType(); JSType falseType = falseNode.getJSType(); if (trueType != null && falseType != null) { n.setJSType(trueType.getLeastSupertype(falseType)); } else { n.setJSType(null); } return scope.createChildFlowScope(); } private FlowScope traverseCall(Node n, FlowScope scope) { scope = traverseChildren(n, scope); Node left = n.getFirstChild(); JSType functionType = getJSType(left).restrictByNotNullOrUndefined(); if (functionType.isFunctionType()) {

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> FunctionType fnType = functionType.toMaybeFunctionType(); n.setJSType(fnType.getReturnType()); backwardsInferenceFromCallSite(n, fnType); } else if (functionType.equals(getNativeType(CHECKED_UNKNOWN_TYPE))) { n.setJSType(getNativeType(CHECKED_UNKNOWN_TYPE)); } scope = tightenTypesAfterAssertions(scope, n); return scope; } private FlowScope tightenTypesAfterAssertions(FlowScope scope, Node callNode) { Node left = callNode.getFirstChild(); Node firstParam = left.getNext(); AssertionFunctionSpec assertionFunctionSpec = assertionFunctionsMap.get(left.getQualifiedName()); if (assertionFunctionSpec == null || firstParam == null) { return scope; } Node assertedNode = assertionFunctionSpec.getAssertedParam(firstParam); if (assertedNode == null) { return scope; } JSType assertedType = assertionFunctionSpec.getAssertedType( callNode, registry); String assertedNodeName = assertedNode.getQualifiedName(); JSType narrowed; // Handle assertions that enforce expressions evaluate to true. if (assertedType == null) { // Handle arbitrary expressions within the assert. scope = reverseInterpreter.getPreciserScopeKnowingConditionOutcome( assertedNode, scope, true); // Build the result of the assertExpression narrowed = getJSType(assertedNode).restrictByNotNullOrUndefined(); } else { // Handle assertions that enforce expressions are of a certain type. JSType type = getJSType(assertedNode); narrowed = type.getGreatestSubtype(assertedType); if (assertedNodeName != null && type.differsFrom(narrowed)) { scope = narrowScope(scope, assertedNode, narrowed); } } if (getJSType(callNode).differsFrom(narrowed)) { callNode.setJSType(narrowed); } return scope; } private FlowScope narrowScope(FlowScope scope, Node node, JSType narrowed) { if (node.isThis()) { // "this" references don't need to be modeled in the control flow graph. return scope;

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>.hasNext()) { maybeResolveTemplatedType( getJSType(declParams.next()), getJSType(callParams.next()), resolvedTypes); } } private void resolvedTemplateType( Map<TemplateType, JSType> map, TemplateType template, JSType resolved) { JSType previous = map.get(template); if (!resolved.isUnknownType()) { if (previous == null) { map.put(template, resolved); } else { JSType join = previous.getLeastSupertype(resolved); map.put(template, join); } } } private static class TemplateTypeReplacer extends ModificationVisitor { private final Map<TemplateType, JSType> replacements; private final JSTypeRegistry registry; TemplateTypeReplacer( JSTypeRegistry registry, Map<TemplateType, JSType> replacements) { super(registry); this.registry = registry; this.replacements = replacements; } @Override public JSType caseTemplateType(TemplateType type) { JSType replacement = replacements.get(type); return replacement != null ? replacement : registry.getNativeType(UNKNOWN_TYPE); } } /** * For functions with function(this: T, ...) and T as parameters, type * inference will set the type of this on a function literal argument to the * the actual type of T. */ private boolean inferTemplatedTypesForCall( Node n, FunctionType fnType) { if (fnType.getTemplateTypeNames().isEmpty()) { return false; } // Try to infer the template types Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters( fnType, n); if (inferred.size() > 0) { // Something useful was found, try to replace it. TemplateTypeReplacer replacer = new TemplateTypeReplacer( registry, inferred); Node callTarget = n.getFirstChild(); FunctionType replacementFnType = fnType.visit(replacer) .toMaybeFunctionType(); Preconditions.checkNotNull(replacementFnType); callTarget.setJSType(replacementFnType); n.setJSType(replacementFnType.getReturnType()); return true; } return false; } private

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> FlowScope traverseNew(Node n, FlowScope scope) { scope = traverseChildren(n, scope); Node constructor = n.getFirstChild(); JSType constructorType = constructor.getJSType(); JSType type = null; if (constructorType != null) { constructorType = constructorType.restrictByNotNullOrUndefined(); if (constructorType.isUnknownType()) { type = getNativeType(UNKNOWN_TYPE); } else { FunctionType ct = constructorType.toMaybeFunctionType(); if (ct == null && constructorType instanceof FunctionType) { // If constructorType is a NoObjectType, then toMaybeFunctionType will // return null. But NoObjectType implements the FunctionType // interface, precisely because it can validly construct objects. ct = (FunctionType) constructorType; } if (ct != null && ct.isConstructor()) { type = ct.getInstanceType(); backwardsInferenceFromCallSite(n, ct); } } } n.setJSType(type); return scope; } private BooleanOutcomePair traverseAnd(Node n, FlowScope scope) { return traverseShortCircuitingBinOp(n, scope, true); } private FlowScope traverseChildren(Node n, FlowScope scope) { for (Node el = n.getFirstChild(); el != null; el = el.getNext()) { scope = traverse(el, scope); } return scope; } private FlowScope traverseGetElem(Node n, FlowScope scope) { scope = traverseChildren(n, scope); ObjectType objType = ObjectType.cast( getJSType(n.getFirstChild()).restrictByNotNullOrUndefined()); if (objType != null) { JSType type = objType.getParameterType(); if (type != null) { n.setJSType(type); } } return dereferencePointer(n.getFirstChild(), scope); } private FlowScope traverseGetProp(Node n, FlowScope scope) { Node objNode = n.getFirstChild(); Node property = n.getLastChild(); scope = traverseChildren(n, scope); n.setJSType( getPropertyType( objNode.getJSType(), property.getString(), n, scope)); return dereferencePointer(n.getFirstChild(), scope);

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> } /** * Suppose X is an object with inferred properties. * Suppose also that X is used in a way where it would only type-check * correctly if some of those properties are widened. * Then we should be polite and automatically widen X's properties for him. * * For a concrete example, consider: * param x {{prop: (number|undefined)}} * function f(x) {} * f({}); * * If we give the anonymous object an inferred property of (number|undefined), * then this code will type-check appropriately. */ private void inferPropertyTypesToMatchConstraint( JSType type, JSType constraint) { if (type == null || constraint == null) { return; } type.matchConstraint(constraint); } /** * If we access a property of a symbol, then that symbol is not * null or undefined. */ private FlowScope dereferencePointer(Node n, FlowScope scope) { if (n.isQualifiedName()) { JSType type = getJSType(n); JSType narrowed = type.restrictByNotNullOrUndefined(); if (type != narrowed) { scope = narrowScope(scope, n, narrowed); } } return scope; } private JSType getPropertyType(JSType objType, String propName, Node n, FlowScope scope) { // We often have a couple of different types to choose from for the // property. Ordered by accuracy, we have // 1) A locally inferred qualified name (which is in the FlowScope) // 2) A globally declared qualified name (which is in the FlowScope) // 3) A property on the owner type (which is on objType) // 4) A name in the type registry (as a last resort) JSType unknownType = getNativeType(UNKNOWN_TYPE); JSType propertyType = null; boolean isLocallyInferred = false; // Scopes sometimes contain inferred type info about qualified names. String qualifiedName = n.getQualifiedName(); StaticSlot<JSType> var = scope.getSlot(qualifiedName); if (var != null) { JSType varType = var.getType(); if (varType != null) {

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> boolean isDeclared = !var.isTypeInferred(); isLocallyInferred = (var != syntacticScope.getSlot(qualifiedName)); if (isDeclared || isLocallyInferred) { propertyType = varType; } } } if (propertyType == null && objType != null) { JSType foundType = objType.findPropertyType(propName); if (foundType != null) { propertyType = foundType; } } if ((propertyType == null || propertyType.isUnknownType()) && qualifiedName != null) { // If we find this node in the registry, then we can infer its type. ObjectType regType = ObjectType.cast(registry.getType(qualifiedName)); if (regType != null) { propertyType = regType.getConstructor(); } } if (propertyType == null) { return getNativeType(UNKNOWN_TYPE); } else if (propertyType.equals(unknownType) && isLocallyInferred) { // If the type has been checked in this scope, // then use CHECKED_UNKNOWN_TYPE instead to indicate that. return getNativeType(CHECKED_UNKNOWN_TYPE); } else { return propertyType; } } private BooleanOutcomePair traverseOr(Node n, FlowScope scope) { return traverseShortCircuitingBinOp(n, scope, false); } private BooleanOutcomePair traverseShortCircuitingBinOp( Node n, FlowScope scope, boolean condition) { Node left = n.getFirstChild(); Node right = n.getLastChild(); // type the left node BooleanOutcomePair leftLiterals = traverseWithinShortCircuitingBinOp(left, scope.createChildFlowScope()); JSType leftType = left.getJSType(); // reverse abstract interpret the left node to produce the correct // scope in which to verify the right node FlowScope rightScope = reverseInterpreter. getPreciserScopeKnowingConditionOutcome( left, leftLiterals.getOutcomeFlowScope(left.getType(), condition), condition); // type the right node BooleanOutcomePair rightLiterals = traverseWithinShortCircuitingBinOp( right, rightScope.createChildFlowScope()); JSType rightType = right.getJSType

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> */ FlowScope getJoinedFlowScope() { if (joinedScope == null) { if (leftScope == rightScope) { joinedScope = rightScope; } else { joinedScope = join(leftScope, rightScope); } } return joinedScope; } /** * Gets the outcome scope if we do know the outcome of the entire * expression. */ FlowScope getOutcomeFlowScope(int nodeType, boolean outcome) { if (nodeType == Token.AND && outcome || nodeType == Token.OR && !outcome) { // We know that the whole expression must have executed. return rightScope; } else { return getJoinedFlowScope(); } } } private BooleanOutcomePair newBooleanOutcomePair( JSType jsType, FlowScope flowScope) { if (jsType == null) { return new BooleanOutcomePair( BooleanLiteralSet.BOTH, BooleanLiteralSet.BOTH, flowScope, flowScope); } return new BooleanOutcomePair(jsType.getPossibleToBooleanOutcomes(), registry.getNativeType(BOOLEAN_TYPE).isSubtype(jsType) ? BooleanLiteralSet.BOTH : BooleanLiteralSet.EMPTY, flowScope, flowScope); } private void redeclareSimpleVar( FlowScope scope, Node nameNode, JSType varType) { Preconditions.checkState(nameNode.isName()); String varName = nameNode.getString(); if (varType == null) { varType = getNativeType(JSTypeNative.UNKNOWN_TYPE); } if (isUnflowable(syntacticScope.getVar(varName))) { return; } scope.inferSlotType(varName, varType); } private boolean isUnflowable(Var v) { return v != null && v.isLocal() && v.isMarkedEscaped() && // It's OK to flow a variable in the scope where it's escaped. v.getScope() == syntacticScope; } /** * This method gets the JSType from the Node argument and verifies that it is * present. */ private JSType getJSType(Node n) { JSType jsType = n.getJSType(); if (jsType == null) { // TODO(nicks

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>antos): This branch indicates a compiler bug, not worthy of // halting the compilation but we should log this and analyze to track // down why it happens. This is not critical and will be resolved over // time as the type checker is extended. return getNativeType(UNKNOWN_TYPE); } else { return jsType; } } private JSType getNativeType(JSTypeNative typeId) { return registry.getNativeType(typeId); } }

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> of a class. These are mostly validated // during TypedScopeCreator, and we only look for the "dumb" cases here. // object.prototype = ...; if (property.equals("prototype")) { if (objectJsType != null && objectJsType.isFunctionType()) { FunctionType functionType = objectJsType.toMaybeFunctionType(); if (functionType.isConstructor()) { JSType rvalueType = rvalue.getJSType(); validator.expectObject(t, rvalue, rvalueType, OVERRIDING_PROTOTYPE_WITH_NON_OBJECT); return; } } } // The generic checks for 'object.property' when 'object' is known, // and 'property' is declared on it. // object.property = ...; ObjectType type = ObjectType.cast( objectJsType.restrictByNotNullOrUndefined()); if (type != null) { if (type.hasProperty(property) && !type.isPropertyTypeInferred(property) && !propertyIsImplicitCast(type, property)) { JSType expectedType = type.getPropertyType(property); if (!expectedType.isUnknownType()) { validator.expectCanAssignToPropertyOf( t, assign, getJSType(rvalue), expectedType, object, property); checkPropertyInheritanceOnGetpropAssign( t, assign, object, property, info, expectedType); return; } } } // If we couldn't get the property type with normal object property // lookups, then check inheritance anyway with the unknown type. checkPropertyInheritanceOnGetpropAssign( t, assign, object, property, info, getNativeType(UNKNOWN_TYPE)); } // Check qualified name sets to 'object' and 'object.property'. // This can sometimes handle cases when the type of 'object' is not known. // e.g., // var obj = createUnknownType(); // /** @type {number} */ obj.foo = true; JSType leftType = getJSType(lvalue); if (lvalue.isQualifiedName()) { // variable with inferred type case JSType rvalueType = getJSType(assign.getLastChild()); Var var = t.getScope().getVar(lvalue.getQualifiedName()); if

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> change the * schema of the object type it is referring to. * * @param t the traversal * @param key the assign node */ private void visitObjLitKey(NodeTraversal t, Node key, Node objlit) { // Do not validate object lit value types in externs. We don't really care, // and it makes it easier to generate externs. if (objlit.isFromExterns()) { ensureTyped(t, key); return; } // TODO(johnlenz): Validate get and set function declarations are valid // as is the functions can have "extraneous" bits. // For getter and setter property definitions the // r-value type != the property type. Node rvalue = key.getFirstChild(); JSType rightType = NodeUtil.getObjectLitKeyTypeFromValueType( key, getJSType(rvalue)); if (rightType == null) { rightType = getNativeType(UNKNOWN_TYPE); } Node owner = objlit; // Validate value is assignable to the key type. JSType keyType = getJSType(key); JSType allowedValueType = keyType; if (allowedValueType.isEnumElementType()) { allowedValueType = allowedValueType.toMaybeEnumElementType().getPrimitiveType(); } boolean valid = validator.expectCanAssignToPropertyOf(t, key, rightType, allowedValueType, owner, NodeUtil.getObjectLitKeyName(key)); if (valid) { ensureTyped(t, key, rightType); } else { ensureTyped(t, key); } // Validate that the key type is assignable to the object property type. // This is necessary as the objlit may have been cast to a non-literal // object type. // TODO(johnlenz): consider introducing a CAST node to the AST (or // perhaps a parentheses node). JSType objlitType = getJSType(objlit); ObjectType type = ObjectType.cast( objlitType.restrictByNotNullOrUndefined()); if (type != null) { String property = NodeUtil.getObjectLitKeyName(key); if (type.hasProperty(property) && !type.isPropertyTypeInferred(property) && !propertyIsImplicitCast(type, property)) { validator

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>ize our compiler errors. String abstractMethodMessage = (abstractMethodName != null) ? ", or " + abstractMethodName : ""; compiler.report( t.makeError(object, INVALID_INTERFACE_MEMBER_DECLARATION, abstractMethodMessage)); } if (assign.getLastChild().isFunction() && !NodeUtil.isEmptyBlock(assign.getLastChild().getLastChild())) { compiler.report( t.makeError(object, INTERFACE_FUNCTION_NOT_EMPTY, abstractMethodName)); } } /** * Visits a NAME node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of the node n. * @return whether the node is typeable or not */ boolean visitName(NodeTraversal t, Node n, Node parent) { // At this stage, we need to determine whether this is a leaf // node in an expression (which therefore needs to have a type // assigned for it) versus some other decorative node that we // can safely ignore. Function names, arguments (children of LP nodes) and // variable declarations are ignored. // TODO(user): remove this short-circuiting in favor of a // pre order traversal of the FUNCTION, CATCH, LP and VAR nodes. int parentNodeType = parent.getType(); if (parentNodeType == Token.FUNCTION || parentNodeType == Token.CATCH || parentNodeType == Token.PARAM_LIST || parentNodeType == Token.VAR) { return false; } JSType type = n.getJSType(); if (type == null) { type = getNativeType(UNKNOWN_TYPE); Var var = t.getScope().getVar(n.getString()); if (var != null) { JSType varType = var.getType(); if (varType != null) { type = varType; } } } ensureTyped(t, n, type); return true; } /** * Visits a GETPROP node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> error reporting. * @param n The node being visited. * @param parent The parent of <code>n</code> */ private void visitGetProp(NodeTraversal t, Node n, Node parent) { // GETPROP nodes have an assigned type on their node by the scope creator // if this is an enum declaration. The only namespaced enum declarations // that we allow are of the form object.name = ...; if (n.getJSType() != null && parent.isAssign()) { return; } // obj.prop or obj.method() // Lots of types can appear on the left, a call to a void function can // never be on the left. getPropertyType will decide what is acceptable // and what isn't. Node property = n.getLastChild(); Node objNode = n.getFirstChild(); JSType childType = getJSType(objNode); // TODO(user): remove in favor of flagging every property access on // non-object. if (!validator.expectNotNullOrUndefined(t, n, childType, "No properties on this expression", getNativeType(OBJECT_TYPE))) { ensureTyped(t, n); return; } checkPropertyAccess(childType, property.getString(), t, n); ensureTyped(t, n); } /** * Emit a warning if we can prove that a property cannot possibly be * defined on an object. Note the difference between JS and a strictly * statically typed language: we're checking if the property * *cannot be defined*, whereas a java compiler would check if the * property *can be undefined*. */ private void checkPropertyAccess(JSType childType, String propName, NodeTraversal t, Node n) { // If the property type is unknown, check the object type to see if it // can ever be defined. We explicitly exclude CHECKED_UNKNOWN (for // properties where we've checked that it exists, or for properties on // objects that aren't in this binary). JSType propType = getJSType(n); if (propType.equals(typeRegistry.getNativeType(UNKNOWN_TYPE))) { childType = childType.autobox(); ObjectType objectType = ObjectType.cast(childType); if (objectType != null

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>) { // We special-case object types so that checks on enums can be // much stricter, and so that we can use hasProperty (which is much // faster in most cases). if (!objectType.hasProperty(propName) || objectType.equals(typeRegistry.getNativeType(UNKNOWN_TYPE))) { if (objectType instanceof EnumType) { report(t, n, INEXISTENT_ENUM_ELEMENT, propName); } else { checkPropertyAccessHelper(objectType, propName, t, n); } } } else { checkPropertyAccessHelper(childType, propName, t, n); } } } private void checkPropertyAccessHelper(JSType objectType, String propName, NodeTraversal t, Node n) { if (!objectType.isEmptyType() && reportMissingProperties && !isPropertyTest(n)) { if (!typeRegistry.canPropertyBeDefined(objectType, propName)) { report(t, n, INEXISTENT_PROPERTY, propName, validator.getReadableJSTypeName(n.getFirstChild(), true)); } } } /** * Determines whether this node is testing for the existence of a property. * If true, we will not emit warnings about a missing property. * * @param getProp The GETPROP being tested. */ private boolean isPropertyTest(Node getProp) { Node parent = getProp.getParent(); switch (parent.getType()) { case Token.CALL: return parent.getFirstChild() != getProp && compiler.getCodingConvention().isPropertyTestFunction(parent); case Token.IF: case Token.WHILE: case Token.DO: case Token.FOR: return NodeUtil.getConditionExpression(parent) == getProp; case Token.INSTANCEOF: case Token.TYPEOF: return true; case Token.AND: case Token.HOOK: return parent.getFirstChild() == getProp; case Token.NOT: return parent.getParent().isOr() && parent.getParent().getFirstChild() == parent; } return false; } /** * Visits a GETELEM node. * * @param t The node traversal object that supplies context, such as

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitGetElem(NodeTraversal t, Node n) { Node left = n.getFirstChild(); Node right = n.getLastChild(); validator.expectIndexMatch(t, n, getJSType(left), getJSType(right)); ensureTyped(t, n); } /** * Visits a VAR node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitVar(NodeTraversal t, Node n) { // TODO(nicksantos): Fix this so that the doc info always shows up // on the NAME node. We probably want to wait for the parser // merge to fix this. JSDocInfo varInfo = n.hasOneChild() ? n.getJSDocInfo() : null; for (Node name : n.children()) { Node value = name.getFirstChild(); // A null var would indicate a bug in the scope creation logic. Var var = t.getScope().getVar(name.getString()); if (value != null) { JSType valueType = getJSType(value); JSType nameType = var.getType(); nameType = (nameType == null) ? getNativeType(UNKNOWN_TYPE) : nameType; JSDocInfo info = name.getJSDocInfo(); if (info == null) { info = varInfo; } checkEnumAlias(t, info, value); if (var.isTypeInferred()) { ensureTyped(t, name, valueType); } else { validator.expectCanAssignTo( t, value, valueType, nameType, "initializing variable"); } } } } /** * Visits a NEW node. */ private void visitNew(NodeTraversal t, Node n) { Node constructor = n.getFirstChild(); JSType type = getJSType(constructor).restrictByNotNullOrUndefined(); if (type.isConstructor() || type.isEmptyType() || type.isUnknownType()) {

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>(); if (jsType == null) { // TODO(nicksantos): This branch indicates a compiler bug, not worthy of // halting the compilation but we should log this and analyze to track // down why it happens. This is not critical and will be resolved over // time as the type checker is extended. return getNativeType(UNKNOWN_TYPE); } else { return jsType; } } // TODO(nicksantos): TypeCheck should never be attaching types to nodes. // All types should be attached by TypeInference. This is not true today // for legacy reasons. There are a number of places where TypeInference // doesn't attach a type, as a signal to TypeCheck that it needs to check // that node's type. /** * Ensure that the given node has a type. If it does not have one, * attach the UNKNOWN_TYPE. */ private void ensureTyped(NodeTraversal t, Node n) { ensureTyped(t, n, getNativeType(UNKNOWN_TYPE)); } private void ensureTyped(NodeTraversal t, Node n, JSTypeNative type) { ensureTyped(t, n, getNativeType(type)); } /** * Enforces type casts, and ensures the node is typed. * * A cast in the way that we use it in JSDoc annotations never * alters the generated code and therefore never can induce any runtime * operation. What this means is that a 'cast' is really just a compile * time constraint on the underlying value. In the future, we may add * support for run-time casts for compiled tests. * * To ensure some shred of sanity, we enforce the notion that the * type you are casting to may only meaningfully be a narrower type * than the underlying declared type. We also invalidate optimizations * on bad type casts. * * @param t The traversal object needed to report errors. * @param n The node getting a type assigned to it. * @param type The type to be assigned. */ private void ensureTyped(NodeTraversal t, Node n, JSType type) { // Make sure FUNCTION nodes always get function type. Preconditions.checkState(!n.isFunction() || type.isFunctionType()

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> properties of this type are also NoResolved types, * and comparisons to other types always have an unknown result. * * @author nicksantos@google.com (Nick Santos) */ class NoResolvedType extends NoType { private static final long serialVersionUID = 1L; NoResolvedType(JSTypeRegistry registry) { super(registry); } @Override public boolean isNoResolvedType() { return true; } @Override public boolean isNoType() { return false; } @Override public JSType getPropertyType(String propertyName) { return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE); } @Override public boolean isSubtype(JSType that) { if (JSType.isSubtypeHelper(this, that)) { return true; } else { return !that.isNoType(); } } @Override String toStringHelper(boolean forAnnotations) { return forAnnotations ? "?" : "NoResolvedType"; } }

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>> slot = getSlot(property); if (slot == null) { return false; } return !slot.isTypeInferred(); } @Override void collectPropertyNames(Set<String> props) { for (String prop : properties.keySet()) { props.add(prop); } ObjectType implicitPrototype = getImplicitPrototype(); if (implicitPrototype != null) { implicitPrototype.collectPropertyNames(props); } } @Override public boolean isPropertyTypeInferred(String property) { StaticSlot<JSType> slot = getSlot(property); if (slot == null) { return false; } return slot.isTypeInferred(); } @Override public JSType getPropertyType(String property) { StaticSlot<JSType> slot = getSlot(property); if (slot == null) { return getNativeType(JSTypeNative.UNKNOWN_TYPE); } return slot.getType(); } @Override public boolean isPropertyInExterns(String propertyName) { Property p = properties.get(propertyName); if (p != null) { return p.isFromExterns(); } ObjectType implicitPrototype = getImplicitPrototype(); if (implicitPrototype != null) { return implicitPrototype.isPropertyInExterns(propertyName); } return false; } @Override boolean defineProperty(String name, JSType type, boolean inferred, Node propertyNode) { if (hasOwnDeclaredProperty(name)) { return false; } Property newProp = new Property( name, type, inferred, propertyNode); Property oldProp = properties.get(name); if (oldProp != null) { // This is to keep previously inferred JsDoc info, e.g., in a // replaceScript scenario. newProp.setJSDocInfo(oldProp.getJSDocInfo()); } properties.put(name, newProp); return true; } @Override public boolean removeProperty(String name) { return properties.remove(name) != null; } @Override public Node getPropertyNode(String propertyName) { Property p = properties.get(propertyName); if (p != null) { return p.getNode(); }

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>SubSubBar.getInstanceType(); final ObjectType googObject = registry.createAnonymousObjectType(); googObject.defineDeclaredProperty("Bar", googBar, null); namedGoogBar.resolve(null, new AbstractStaticScope<JSType>() { @Override public StaticSlot<JSType> getSlot(String name) { if ("goog".equals(name)) { return new SimpleSlot("goog", googObject, false); } else { return null; } } }); assertNotNull(namedGoogBar.getImplicitPrototype()); forwardDeclaredNamedType = new NamedType(registry, "forwardDeclared", "source", 1, 0); registry.forwardDeclareType("forwardDeclared"); forwardDeclaredNamedType.resolve( new SimpleErrorReporter(), EMPTY_SCOPE); types = ImmutableList.of( NO_OBJECT_TYPE, NO_RESOLVED_TYPE, NO_TYPE, BOOLEAN_OBJECT_TYPE, BOOLEAN_TYPE, STRING_OBJECT_TYPE, STRING_TYPE, VOID_TYPE, UNKNOWN_TYPE, NULL_TYPE, NUMBER_OBJECT_TYPE, NUMBER_TYPE, DATE_TYPE, ERROR_TYPE, SYNTAX_ERROR_TYPE, dateMethod, functionType, unresolvedNamedType, googBar, googSubBar, googSubSubBar, namedGoogBar, googBar.getInstanceType(), subclassOfUnresolvedNamedType, subclassCtor, recordType, enumType, elementsType, googBar, googSubBar, forwardDeclaredNamedType); } /** * Tests the behavior of the top constructor type. */ public void testUniversalConstructorType() throws Exception { // isXxx assertFalse(U2U_CONSTRUCTOR_TYPE.isNoObjectType()); assertFalse(U2U_CONSTRUCTOR_TYPE.isNoType()); assertFalse(U2U_CONSTRUCTOR_TYPE.isArrayType()); assertFalse(U2U_CONSTRUCTOR_TYPE.isBooleanValueType()); assertFalse(U2U_CONSTRUCTOR_TYPE.isDateType()); assertFalse(U2U_CONSTRUCTOR_TYPE.isEnumElementType()); assertFalse(U2U_CONSTRUCTOR_TYPE.isNullType()); assertFalse(U2U_CONSTRUCTOR_TYPE.isNamedType

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>U2U_CONSTRUCTOR_TYPE.matchesObjectContext()); assertFalse(U2U_CONSTRUCTOR_TYPE.matchesStringContext()); assertFalse(U2U_CONSTRUCTOR_TYPE.matchesUint32Context()); // toString assertEquals("Function", U2U_CONSTRUCTOR_TYPE.toString()); assertTrue(U2U_CONSTRUCTOR_TYPE.hasDisplayName()); assertEquals("Function", U2U_CONSTRUCTOR_TYPE.getDisplayName()); // getPropertyType assertTypeEquals(UNKNOWN_TYPE, U2U_CONSTRUCTOR_TYPE.getPropertyType("anyProperty")); assertTrue(U2U_CONSTRUCTOR_TYPE.isNativeObjectType()); Asserts.assertResolvesToSame(U2U_CONSTRUCTOR_TYPE); assertTrue(U2U_CONSTRUCTOR_TYPE.isNominalConstructor()); } /** * Tests the behavior of the Bottom Object type. */ public void testNoObjectType() throws Exception { // isXxx assertTrue(NO_OBJECT_TYPE.isNoObjectType()); assertFalse(NO_OBJECT_TYPE.isNoType()); assertFalse(NO_OBJECT_TYPE.isArrayType()); assertFalse(NO_OBJECT_TYPE.isBooleanValueType()); assertFalse(NO_OBJECT_TYPE.isDateType()); assertFalse(NO_OBJECT_TYPE.isEnumElementType()); assertFalse(NO_OBJECT_TYPE.isNullType()); assertFalse(NO_OBJECT_TYPE.isNamedType()); assertFalse(NO_OBJECT_TYPE.isNullType()); assertTrue(NO_OBJECT_TYPE.isNumber()); assertFalse(NO_OBJECT_TYPE.isNumberObjectType()); assertFalse(NO_OBJECT_TYPE.isNumberValueType()); assertTrue(NO_OBJECT_TYPE.isObject()); assertFalse(NO_OBJECT_TYPE.isFunctionPrototypeType()); assertFalse(NO_OBJECT_TYPE.isRegexpType()); assertTrue(NO_OBJECT_TYPE.isString()); assertFalse(NO_OBJECT_TYPE.isStringObjectType()); assertFalse(NO_OBJECT_TYPE.isStringValueType()); assertFalse(NO_OBJECT_TYPE.isEnumType()); assertFalse(NO_OBJECT_TYPE.isUnionType()); assertFalse(NO_OBJECT_TYPE.isAllType()); assertFalse(NO_OBJECT_TYPE.isVoidType()); assertTrue(NO_OBJECT_TYPE.isConstructor()); assertFalse(NO_OBJECT_TYPE.isInstanceType());

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>(SYNTAX_ERROR_TYPE)); assertTrue(NO_RESOLVED_TYPE.canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); assertTrue(NO_RESOLVED_TYPE.canTestForShallowEqualityWith(ALL_TYPE)); assertTrue(NO_RESOLVED_TYPE.canTestForShallowEqualityWith(VOID_TYPE)); // isNullable assertTrue(NO_RESOLVED_TYPE.isNullable()); // isObject assertTrue(NO_RESOLVED_TYPE.isObject()); // matchesXxx assertTrue(NO_RESOLVED_TYPE.matchesInt32Context()); assertTrue(NO_RESOLVED_TYPE.matchesNumberContext()); assertTrue(NO_RESOLVED_TYPE.matchesObjectContext()); assertTrue(NO_RESOLVED_TYPE.matchesStringContext()); assertTrue(NO_RESOLVED_TYPE.matchesUint32Context()); // toString assertEquals("NoResolvedType", NO_RESOLVED_TYPE.toString()); assertEquals(null, NO_RESOLVED_TYPE.getDisplayName()); assertFalse(NO_RESOLVED_TYPE.hasDisplayName()); // getPropertyType assertTypeEquals(CHECKED_UNKNOWN_TYPE, NO_RESOLVED_TYPE.getPropertyType("anyProperty")); Asserts.assertResolvesToSame(NO_RESOLVED_TYPE); assertTrue(forwardDeclaredNamedType.isEmptyType()); assertTrue(forwardDeclaredNamedType.isNoResolvedType()); } /** * Tests the behavior of the Array type. */ public void testArrayType() throws Exception { // isXxx assertTrue(ARRAY_TYPE.isArrayType()); assertFalse(ARRAY_TYPE.isBooleanValueType()); assertFalse(ARRAY_TYPE.isDateType()); assertFalse(ARRAY_TYPE.isEnumElementType()); assertFalse(ARRAY_TYPE.isNamedType()); assertFalse(ARRAY_TYPE.isNullType()); assertFalse(ARRAY_TYPE.isNumber()); assertFalse(ARRAY_TYPE.isNumberObjectType()); assertFalse(ARRAY_TYPE.isNumberValueType()); assertTrue(ARRAY_TYPE.isObject()); assertFalse(ARRAY_TYPE.isFunctionPrototypeType()); assertTrue(ARRAY_TYPE.getImplicitPrototype().isFunctionPrototypeType()); assertFalse(ARRAY_TYPE.isRegexpType()); assertFalse(ARRAY_TYPE.isString()); assertFalse(ARRAY_TYPE.isStringObjectType()); assertFalse(ARRAY_TYPE.isStringValueType()); assertFalse(ARRAY_

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>LeastSupertype(ALL_TYPE)); assertTypeEquals(createUnionType(STRING_OBJECT_TYPE, ARRAY_TYPE), ARRAY_TYPE.getLeastSupertype(STRING_OBJECT_TYPE)); assertTypeEquals(createUnionType(NUMBER_TYPE, ARRAY_TYPE), ARRAY_TYPE.getLeastSupertype(NUMBER_TYPE)); assertTypeEquals(createUnionType(ARRAY_TYPE, functionType), ARRAY_TYPE.getLeastSupertype(functionType)); assertTypeEquals(OBJECT_TYPE, ARRAY_TYPE.getLeastSupertype(OBJECT_TYPE)); assertTypeEquals(createUnionType(DATE_TYPE, ARRAY_TYPE), ARRAY_TYPE.getLeastSupertype(DATE_TYPE)); assertTypeEquals(createUnionType(REGEXP_TYPE, ARRAY_TYPE), ARRAY_TYPE.getLeastSupertype(REGEXP_TYPE)); // getPropertyType assertEquals(17, ARRAY_TYPE.getImplicitPrototype().getPropertiesCount()); assertEquals(18, ARRAY_TYPE.getPropertiesCount()); assertReturnTypeEquals(ARRAY_TYPE, ARRAY_TYPE.getPropertyType("constructor")); assertReturnTypeEquals(STRING_TYPE, ARRAY_TYPE.getPropertyType("toString")); assertReturnTypeEquals(STRING_TYPE, ARRAY_TYPE.getPropertyType("toLocaleString")); assertReturnTypeEquals(ARRAY_TYPE, ARRAY_TYPE.getPropertyType("concat")); assertReturnTypeEquals(STRING_TYPE, ARRAY_TYPE.getPropertyType("join")); assertReturnTypeEquals(UNKNOWN_TYPE, ARRAY_TYPE.getPropertyType("pop")); assertReturnTypeEquals(NUMBER_TYPE, ARRAY_TYPE.getPropertyType("push")); assertReturnTypeEquals(ARRAY_TYPE, ARRAY_TYPE.getPropertyType("reverse")); assertReturnTypeEquals(UNKNOWN_TYPE, ARRAY_TYPE.getPropertyType("shift")); assertReturnTypeEquals(ARRAY_TYPE, ARRAY_TYPE.getPropertyType("slice")); assertReturnTypeEquals(ARRAY_TYPE, ARRAY_TYPE.getPropertyType("sort")); assertReturnTypeEquals(ARRAY_TYPE, ARRAY_TYPE.getPropertyType("splice")); assertReturnTypeEquals(NUMBER_TYPE, ARRAY_TYPE.getPropertyType("unshift")); assertTypeEquals(NUMBER_TYPE, ARRAY_TYPE.getPropertyType("length")); // isPropertyType* assertPropertyTypeDeclared(ARRAY_TYPE, "pop"); // matchesXxx assertFalse(ARRAY_TYPE.

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>matchesInt32Context()); assertFalse(ARRAY_TYPE.matchesNumberContext()); assertTrue(ARRAY_TYPE.matchesObjectContext()); assertTrue(ARRAY_TYPE.matchesStringContext()); assertFalse(ARRAY_TYPE.matchesUint32Context()); // toString assertEquals("Array", ARRAY_TYPE.toString()); assertTrue(ARRAY_TYPE.hasDisplayName()); assertEquals("Array", ARRAY_TYPE.getDisplayName()); assertTrue(ARRAY_TYPE.isNativeObjectType()); Asserts.assertResolvesToSame(ARRAY_TYPE); assertFalse(ARRAY_TYPE.isNominalConstructor()); assertTrue(ARRAY_TYPE.getConstructor().isNominalConstructor()); } /** * Tests the behavior of the unknown type. */ public void testUnknownType() throws Exception { // isXxx assertFalse(UNKNOWN_TYPE.isArrayType()); assertFalse(UNKNOWN_TYPE.isBooleanObjectType()); assertFalse(UNKNOWN_TYPE.isBooleanValueType()); assertFalse(UNKNOWN_TYPE.isDateType()); assertFalse(UNKNOWN_TYPE.isEnumElementType()); assertFalse(UNKNOWN_TYPE.isNamedType()); assertFalse(UNKNOWN_TYPE.isNullType()); assertFalse(UNKNOWN_TYPE.isNumberObjectType()); assertFalse(UNKNOWN_TYPE.isNumberValueType()); assertTrue(UNKNOWN_TYPE.isObject()); assertFalse(UNKNOWN_TYPE.isFunctionPrototypeType()); assertFalse(UNKNOWN_TYPE.isRegexpType()); assertFalse(UNKNOWN_TYPE.isStringObjectType()); assertFalse(UNKNOWN_TYPE.isStringValueType()); assertFalse(UNKNOWN_TYPE.isEnumType()); assertFalse(UNKNOWN_TYPE.isUnionType()); assertTrue(UNKNOWN_TYPE.isUnknownType()); assertFalse(UNKNOWN_TYPE.isVoidType()); assertFalse(UNKNOWN_TYPE.isConstructor()); assertFalse(UNKNOWN_TYPE.isInstanceType()); // autoboxesTo assertNull(UNKNOWN_TYPE.autoboxesTo()); // canAssignTo assertTrue(UNKNOWN_TYPE.canAssignTo(UNKNOWN_TYPE)); assertTrue(UNKNOWN_TYPE.canAssignTo(STRING_TYPE)); assertTrue(UNKNOWN_TYPE.canAssignTo(NUMBER_TYPE)); assertTrue(UNKNOWN_TYPE.canAssignTo(functionType)); assertTrue(UNKNOWN_TYPE.canAssignTo(recordType)); assertTrue(UNKNOWN_TYPE.canAssignTo(NULL_TYPE)); assertTrue(UNKNOWN_TYPE.canAssignTo(OBJECT_TYPE)); assertTrue(UNKNOWN_

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>TYPE.canAssignTo(DATE_TYPE)); assertTrue(UNKNOWN_TYPE.canAssignTo(namedGoogBar)); assertTrue(UNKNOWN_TYPE.canAssignTo(unresolvedNamedType)); assertTrue(UNKNOWN_TYPE.canAssignTo(REGEXP_TYPE)); assertTrue(UNKNOWN_TYPE.canAssignTo(VOID_TYPE)); // canBeCalled assertTrue(UNKNOWN_TYPE.canBeCalled()); // canTestForEqualityWith assertCanTestForEqualityWith(UNKNOWN_TYPE, UNKNOWN_TYPE); assertCanTestForEqualityWith(UNKNOWN_TYPE, STRING_TYPE); assertCanTestForEqualityWith(UNKNOWN_TYPE, NUMBER_TYPE); assertCanTestForEqualityWith(UNKNOWN_TYPE, functionType); assertCanTestForEqualityWith(UNKNOWN_TYPE, recordType); assertCanTestForEqualityWith(UNKNOWN_TYPE, VOID_TYPE); assertCanTestForEqualityWith(UNKNOWN_TYPE, OBJECT_TYPE); assertCanTestForEqualityWith(UNKNOWN_TYPE, DATE_TYPE); assertCanTestForEqualityWith(UNKNOWN_TYPE, REGEXP_TYPE); assertCanTestForEqualityWith(UNKNOWN_TYPE, BOOLEAN_TYPE); // canTestForShallowEqualityWith assertTrue(UNKNOWN_TYPE.canTestForShallowEqualityWith(UNKNOWN_TYPE)); assertTrue(UNKNOWN_TYPE.canTestForShallowEqualityWith(STRING_TYPE)); assertTrue(UNKNOWN_TYPE.canTestForShallowEqualityWith(NUMBER_TYPE)); assertTrue(UNKNOWN_TYPE.canTestForShallowEqualityWith(functionType)); assertTrue(UNKNOWN_TYPE.canTestForShallowEqualityWith(recordType)); assertTrue(UNKNOWN_TYPE.canTestForShallowEqualityWith(VOID_TYPE)); assertTrue(UNKNOWN_TYPE.canTestForShallowEqualityWith(OBJECT_TYPE)); assertTrue(UNKNOWN_TYPE.canTestForShallowEqualityWith(DATE_TYPE)); assertTrue(UNKNOWN_TYPE.canTestForShallowEqualityWith(REGEXP_TYPE)); // canHaveNullValue assertTrue(UNKNOWN_TYPE.isNullable()); // getGreatestCommonType assertTypeEquals(UNKNOWN_TYPE, UNKNOWN_TYPE.getLeastSupertype(UNKNOWN_TYPE)); assertTypeEquals(UNKNOWN_TYPE, UNKNOWN_TYPE.getLeastSupertype(STRING_TYPE)); assertTypeEquals(UNKNOWN_

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>TYPE, UNKNOWN_TYPE.getLeastSupertype(NUMBER_TYPE)); assertTypeEquals(UNKNOWN_TYPE, UNKNOWN_TYPE.getLeastSupertype(functionType)); assertTypeEquals(UNKNOWN_TYPE, UNKNOWN_TYPE.getLeastSupertype(OBJECT_TYPE)); assertTypeEquals(UNKNOWN_TYPE, UNKNOWN_TYPE.getLeastSupertype(DATE_TYPE)); assertTypeEquals(UNKNOWN_TYPE, UNKNOWN_TYPE.getLeastSupertype(REGEXP_TYPE)); // matchesXxx assertTrue(UNKNOWN_TYPE.matchesInt32Context()); assertTrue(UNKNOWN_TYPE.matchesNumberContext()); assertTrue(UNKNOWN_TYPE.matchesObjectContext()); assertTrue(UNKNOWN_TYPE.matchesStringContext()); assertTrue(UNKNOWN_TYPE.matchesUint32Context()); // isPropertyType* assertPropertyTypeUnknown(UNKNOWN_TYPE, "XXX"); // toString assertEquals("?", UNKNOWN_TYPE.toString()); assertTrue(UNKNOWN_TYPE.hasDisplayName()); assertEquals("Unknown", UNKNOWN_TYPE.getDisplayName()); Asserts.assertResolvesToSame(UNKNOWN_TYPE); assertFalse(UNKNOWN_TYPE.isNominalConstructor()); } /** * Tests the behavior of the unknown type. */ public void testAllType() throws Exception { // isXxx assertFalse(ALL_TYPE.isArrayType()); assertFalse(ALL_TYPE.isBooleanValueType()); assertFalse(ALL_TYPE.isDateType()); assertFalse(ALL_TYPE.isEnumElementType()); assertFalse(ALL_TYPE.isNamedType()); assertFalse(ALL_TYPE.isNullType()); assertFalse(ALL_TYPE.isNumber()); assertFalse(ALL_TYPE.isNumberObjectType()); assertFalse(ALL_TYPE.isNumberValueType()); assertFalse(ALL_TYPE.isObject()); assertFalse(ALL_TYPE.isFunctionPrototypeType()); assertFalse(ALL_TYPE.isRegexpType()); assertFalse(ALL_TYPE.isString()); assertFalse(ALL_TYPE.isStringObjectType()); assertFalse(ALL_TYPE.isStringValueType()); assertFalse(ALL_TYPE.isEnumType()); assertFalse(ALL_TYPE.isUnionType()); assertTrue(ALL_TYPE.isAllType()); assertFalse(ALL_TYPE.isVoidType()); assertFalse(ALL_TYPE.isConstructor()); assertFalse(ALL_TYPE.isInstanceType()); // canAssignTo assertFalse(ALL_TYPE

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>.canAssignTo(NO_TYPE)); assertFalse(ALL_TYPE.canAssignTo(NO_OBJECT_TYPE)); assertTrue(ALL_TYPE.canAssignTo(ALL_TYPE)); assertFalse(ALL_TYPE.canAssignTo(STRING_OBJECT_TYPE)); assertFalse(ALL_TYPE.canAssignTo(NUMBER_TYPE)); assertFalse(ALL_TYPE.canAssignTo(functionType)); assertFalse(ALL_TYPE.canAssignTo(recordType)); assertFalse(ALL_TYPE.canAssignTo(NULL_TYPE)); assertFalse(ALL_TYPE.canAssignTo(OBJECT_TYPE)); assertFalse(ALL_TYPE.canAssignTo(DATE_TYPE)); assertTrue(ALL_TYPE.canAssignTo(unresolvedNamedType)); assertFalse(ALL_TYPE.canAssignTo(namedGoogBar)); assertFalse(ALL_TYPE.canAssignTo(REGEXP_TYPE)); assertFalse(ALL_TYPE.canAssignTo(VOID_TYPE)); assertTrue(ALL_TYPE.canAssignTo(UNKNOWN_TYPE)); // canBeCalled assertFalse(ALL_TYPE.canBeCalled()); // canTestForEqualityWith assertCanTestForEqualityWith(ALL_TYPE, ALL_TYPE); assertCanTestForEqualityWith(ALL_TYPE, STRING_OBJECT_TYPE); assertCanTestForEqualityWith(ALL_TYPE, NUMBER_TYPE); assertCanTestForEqualityWith(ALL_TYPE, functionType); assertCanTestForEqualityWith(ALL_TYPE, recordType); assertCanTestForEqualityWith(ALL_TYPE, VOID_TYPE); assertCanTestForEqualityWith(ALL_TYPE, OBJECT_TYPE); assertCanTestForEqualityWith(ALL_TYPE, DATE_TYPE); assertCanTestForEqualityWith(ALL_TYPE, REGEXP_TYPE); // canTestForShallowEqualityWith assertTrue(ALL_TYPE.canTestForShallowEqualityWith(NO_TYPE)); assertTrue(ALL_TYPE.canTestForShallowEqualityWith(NO_OBJECT_TYPE)); assertTrue(ALL_TYPE.canTestForShallowEqualityWith(ARRAY_TYPE)); assertTrue(ALL_TYPE.canTestForShallowEqualityWith(BOOLEAN_TYPE)); assertTrue(ALL_TYPE.canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); assertTrue(ALL_TYPE.canTest

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>ForShallowEqualityWith(DATE_TYPE)); assertTrue(ALL_TYPE.canTestForShallowEqualityWith(ERROR_TYPE)); assertTrue(ALL_TYPE.canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); assertTrue(ALL_TYPE.canTestForShallowEqualityWith(functionType)); assertTrue(ALL_TYPE.canTestForShallowEqualityWith(recordType)); assertTrue(ALL_TYPE.canTestForShallowEqualityWith(NULL_TYPE)); assertTrue(ALL_TYPE.canTestForShallowEqualityWith(NUMBER_TYPE)); assertTrue(ALL_TYPE.canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); assertTrue(ALL_TYPE.canTestForShallowEqualityWith(OBJECT_TYPE)); assertTrue(ALL_TYPE.canTestForShallowEqualityWith(URI_ERROR_TYPE)); assertTrue(ALL_TYPE.canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); assertTrue(ALL_TYPE.canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); assertTrue(ALL_TYPE.canTestForShallowEqualityWith(REGEXP_TYPE)); assertTrue(ALL_TYPE.canTestForShallowEqualityWith(STRING_TYPE)); assertTrue(ALL_TYPE.canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); assertTrue(ALL_TYPE.canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); assertTrue(ALL_TYPE.canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); assertTrue(ALL_TYPE.canTestForShallowEqualityWith(ALL_TYPE)); assertTrue(ALL_TYPE.canTestForShallowEqualityWith(VOID_TYPE)); // isNullable assertFalse(ALL_TYPE.isNullable()); // getLeastSupertype assertTypeEquals(ALL_TYPE, ALL_TYPE.getLeastSupertype(ALL_TYPE)); assertTypeEquals(ALL_TYPE, ALL_TYPE.getLeastSupertype(UNKNOWN_TYPE)); assertTypeEquals(ALL_TYPE, ALL_TYPE.getLeastSupertype(STRING_OBJECT_TYPE)); assertTypeEquals(ALL_TYPE, ALL_TYPE.getLeastSupertype(NUMBER_TYPE)); assertTypeEquals(ALL_TYPE, ALL_TYPE.getLeastSupertype(functionType)); assertTypeEquals(

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>()); assertTrue(OBJECT_TYPE.isInstanceType()); // canAssignTo assertFalse(OBJECT_TYPE.canAssignTo(NO_TYPE)); assertTrue(OBJECT_TYPE.canAssignTo(ALL_TYPE)); assertFalse(OBJECT_TYPE.canAssignTo(STRING_OBJECT_TYPE)); assertFalse(OBJECT_TYPE.canAssignTo(NUMBER_TYPE)); assertFalse(OBJECT_TYPE.canAssignTo(functionType)); assertFalse(OBJECT_TYPE.canAssignTo(recordType)); assertFalse(OBJECT_TYPE.canAssignTo(NULL_TYPE)); assertTrue(OBJECT_TYPE.canAssignTo(OBJECT_TYPE)); assertFalse(OBJECT_TYPE.canAssignTo(DATE_TYPE)); assertFalse(OBJECT_TYPE.canAssignTo(namedGoogBar)); assertTrue(OBJECT_TYPE.canAssignTo(unresolvedNamedType)); assertFalse(OBJECT_TYPE.canAssignTo(REGEXP_TYPE)); assertFalse(OBJECT_TYPE.canAssignTo(ARRAY_TYPE)); assertTrue(OBJECT_TYPE.canAssignTo(UNKNOWN_TYPE)); // canBeCalled assertFalse(OBJECT_TYPE.canBeCalled()); // canTestForEqualityWith assertCanTestForEqualityWith(OBJECT_TYPE, ALL_TYPE); assertCanTestForEqualityWith(OBJECT_TYPE, STRING_OBJECT_TYPE); assertCanTestForEqualityWith(OBJECT_TYPE, NUMBER_TYPE); assertCanTestForEqualityWith(OBJECT_TYPE, STRING_TYPE); assertCanTestForEqualityWith(OBJECT_TYPE, BOOLEAN_TYPE); assertCanTestForEqualityWith(OBJECT_TYPE, functionType); assertCanTestForEqualityWith(OBJECT_TYPE, recordType); assertCannotTestForEqualityWith(OBJECT_TYPE, VOID_TYPE); assertCanTestForEqualityWith(OBJECT_TYPE, OBJECT_TYPE); assertCanTestForEqualityWith(OBJECT_TYPE, DATE_TYPE); assertCanTestForEqualityWith(OBJECT_TYPE, REGEXP_TYPE); assertCanTestForEqualityWith(OBJECT_TYPE, ARRAY_TYPE); assertCanTestForEqualityWith(OBJECT_TYPE, UNKNOWN_TYPE); // canTestForShallowEqualityWith assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(NO_TYPE)); assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(NO_OBJECT_

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>TYPE)); assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(ARRAY_TYPE)); assertFalse(OBJECT_TYPE.canTestForShallowEqualityWith(BOOLEAN_TYPE)); assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(DATE_TYPE)); assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(ERROR_TYPE)); assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(functionType)); assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(recordType)); assertFalse(OBJECT_TYPE.canTestForShallowEqualityWith(NULL_TYPE)); assertFalse(OBJECT_TYPE.canTestForShallowEqualityWith(NUMBER_TYPE)); assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(OBJECT_TYPE)); assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(URI_ERROR_TYPE)); assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); assertTrue(OBJECT_TYPE. canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(REGEXP_TYPE)); assertFalse(OBJECT_TYPE.canTestForShallowEqualityWith(STRING_TYPE)); assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(ALL_TYPE)); assertFalse(OBJECT_TYPE.canTestForShallowEqualityWith(VOID_TYPE)); assertTrue(OBJECT_TYPE.canTestForShallowEqualityWith(UNKNOWN_TYPE)); // isNullable assertFalse(OBJECT_TYPE.isNullable()); // getLeastSupertype assertTypeEquals(ALL_TYPE, OBJECT_TYPE.getLeastSupertype(ALL_TYPE)); assertTypeEquals(OBJECT_TYPE

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>, OBJECT_TYPE.getLeastSupertype(STRING_OBJECT_TYPE)); assertTypeEquals(createUnionType(OBJECT_TYPE, NUMBER_TYPE), OBJECT_TYPE.getLeastSupertype(NUMBER_TYPE)); assertTypeEquals(OBJECT_TYPE, OBJECT_TYPE.getLeastSupertype(functionType)); assertTypeEquals(OBJECT_TYPE, OBJECT_TYPE.getLeastSupertype(OBJECT_TYPE)); assertTypeEquals(OBJECT_TYPE, OBJECT_TYPE.getLeastSupertype(DATE_TYPE)); assertTypeEquals(OBJECT_TYPE, OBJECT_TYPE.getLeastSupertype(REGEXP_TYPE)); // getPropertyType assertEquals(7, OBJECT_TYPE.getPropertiesCount()); assertReturnTypeEquals(OBJECT_TYPE, OBJECT_TYPE.getPropertyType("constructor")); assertReturnTypeEquals(STRING_TYPE, OBJECT_TYPE.getPropertyType("toString")); assertReturnTypeEquals(STRING_TYPE, OBJECT_TYPE.getPropertyType("toLocaleString")); assertReturnTypeEquals(UNKNOWN_TYPE, OBJECT_TYPE.getPropertyType("valueOf")); assertReturnTypeEquals(BOOLEAN_TYPE, OBJECT_TYPE.getPropertyType("hasOwnProperty")); assertReturnTypeEquals(BOOLEAN_TYPE, OBJECT_TYPE.getPropertyType("isPrototypeOf")); assertReturnTypeEquals(BOOLEAN_TYPE, OBJECT_TYPE.getPropertyType("propertyIsEnumerable")); // matchesXxx assertFalse(OBJECT_TYPE.matchesInt32Context()); assertFalse(OBJECT_TYPE.matchesNumberContext()); assertTrue(OBJECT_TYPE.matchesObjectContext()); assertTrue(OBJECT_TYPE.matchesStringContext()); assertFalse(OBJECT_TYPE.matchesUint32Context()); // implicit prototype assertTypeEquals(OBJECT_PROTOTYPE, OBJECT_TYPE.getImplicitPrototype()); // toString assertEquals("Object", OBJECT_TYPE.toString()); assertTrue(OBJECT_TYPE.isNativeObjectType()); assertTrue(OBJECT_TYPE.getImplicitPrototype().isNativeObjectType()); Asserts.assertResolvesToSame(OBJECT_TYPE); assertFalse(OBJECT_TYPE.isNominalConstructor()); assertTrue(OBJECT_TYPE.getConstructor().isNominalConstructor()); } /** * Tests the behavior of the number value type. */ public void testNumberObjectType() throws Exception { // isXxx assertFalse(NUMBER_OBJECT_TYPE.isArrayType()); assertFalse(NUMBER_OBJECT

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>))); assertFalse(NUMBER_OBJECT_TYPE.canAssignTo( createUnionType(NUMBER_TYPE, NULL_TYPE))); assertTrue(NUMBER_OBJECT_TYPE.canAssignTo(UNKNOWN_TYPE)); // canBeCalled assertFalse(NUMBER_OBJECT_TYPE.canBeCalled()); // canTestForEqualityWith assertCanTestForEqualityWith(NUMBER_OBJECT_TYPE, NO_TYPE); assertCanTestForEqualityWith(NUMBER_OBJECT_TYPE, NO_OBJECT_TYPE); assertCanTestForEqualityWith(NUMBER_OBJECT_TYPE, ALL_TYPE); assertCanTestForEqualityWith(NUMBER_OBJECT_TYPE, NUMBER_TYPE); assertCanTestForEqualityWith(NUMBER_OBJECT_TYPE, STRING_OBJECT_TYPE); assertCanTestForEqualityWith(NUMBER_OBJECT_TYPE, functionType); assertCanTestForEqualityWith(NUMBER_OBJECT_TYPE, elementsType); assertCannotTestForEqualityWith(NUMBER_OBJECT_TYPE, VOID_TYPE); assertCanTestForEqualityWith(NUMBER_OBJECT_TYPE, OBJECT_TYPE); assertCanTestForEqualityWith(NUMBER_OBJECT_TYPE, DATE_TYPE); assertCanTestForEqualityWith(NUMBER_OBJECT_TYPE, REGEXP_TYPE); assertCanTestForEqualityWith(NUMBER_OBJECT_TYPE, ARRAY_TYPE); // canTestForShallowEqualityWith assertTrue(NUMBER_OBJECT_TYPE.canTestForShallowEqualityWith(NO_TYPE)); assertTrue(NUMBER_OBJECT_TYPE. canTestForShallowEqualityWith(NO_OBJECT_TYPE)); assertFalse(NUMBER_OBJECT_TYPE.canTestForShallowEqualityWith(ARRAY_TYPE)); assertFalse(NUMBER_OBJECT_TYPE.canTestForShallowEqualityWith(BOOLEAN_TYPE)); assertFalse(NUMBER_OBJECT_TYPE. canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); assertFalse(NUMBER_OBJECT_TYPE.canTestForShallowEqualityWith(DATE_TYPE)); assertFalse(NUMBER_OBJECT_TYPE.canTestForShallowEqualityWith(ERROR_TYPE)); assertFalse(NUMBER_OBJECT_TYPE. canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); assertFalse(NUMBER_OBJECT_TYPE.canTestForShallowEqualityWith(functionType)); assertFalse(NUMBER_OBJECT_TYPE.canTestForShallowEquality

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>canAssignTo(NUMBER_TYPE)); assertFalse(NUMBER_TYPE.canAssignTo(functionType)); assertFalse(NUMBER_TYPE.canAssignTo(NULL_TYPE)); assertFalse(NUMBER_TYPE.canAssignTo(OBJECT_TYPE)); assertFalse(NUMBER_TYPE.canAssignTo(DATE_TYPE)); assertTrue(NUMBER_TYPE.canAssignTo(unresolvedNamedType)); assertFalse(NUMBER_TYPE.canAssignTo(namedGoogBar)); assertTrue(NUMBER_TYPE.canAssignTo( createUnionType(NUMBER_TYPE, NULL_TYPE))); assertTrue(NUMBER_TYPE.canAssignTo(UNKNOWN_TYPE)); // canBeCalled assertFalse(NUMBER_TYPE.canBeCalled()); // canTestForEqualityWith assertCanTestForEqualityWith(NUMBER_TYPE, NO_TYPE); assertCanTestForEqualityWith(NUMBER_TYPE, NO_OBJECT_TYPE); assertCanTestForEqualityWith(NUMBER_TYPE, ALL_TYPE); assertCanTestForEqualityWith(NUMBER_TYPE, NUMBER_TYPE); assertCanTestForEqualityWith(NUMBER_TYPE, STRING_OBJECT_TYPE); assertCannotTestForEqualityWith(NUMBER_TYPE, functionType); assertCannotTestForEqualityWith(NUMBER_TYPE, VOID_TYPE); assertCanTestForEqualityWith(NUMBER_TYPE, OBJECT_TYPE); assertCanTestForEqualityWith(NUMBER_TYPE, DATE_TYPE); assertCanTestForEqualityWith(NUMBER_TYPE, REGEXP_TYPE); assertCanTestForEqualityWith(NUMBER_TYPE, ARRAY_TYPE); assertCanTestForEqualityWith(NUMBER_TYPE, UNKNOWN_TYPE); // canTestForShallowEqualityWith assertTrue(NUMBER_TYPE.canTestForShallowEqualityWith(NO_TYPE)); assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(NO_OBJECT_TYPE)); assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(ARRAY_TYPE)); assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(BOOLEAN_TYPE)); assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(DATE_TYPE)); assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(ERROR_TYPE)); assertFalse(NUMBER_TYPE

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>.canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(functionType)); assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(NULL_TYPE)); assertTrue(NUMBER_TYPE.canTestForShallowEqualityWith(NUMBER_TYPE)); assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(OBJECT_TYPE)); assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(URI_ERROR_TYPE)); assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); assertFalse(NUMBER_TYPE. canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(REGEXP_TYPE)); assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(STRING_TYPE)); assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); assertTrue(NUMBER_TYPE.canTestForShallowEqualityWith(ALL_TYPE)); assertFalse(NUMBER_TYPE.canTestForShallowEqualityWith(VOID_TYPE)); assertTrue(NUMBER_TYPE.canTestForShallowEqualityWith(UNKNOWN_TYPE)); // isNullable assertFalse(NUMBER_TYPE.isNullable()); // getLeastSupertype assertTypeEquals(ALL_TYPE, NUMBER_TYPE.getLeastSupertype(ALL_TYPE)); assertTypeEquals(createUnionType(NUMBER_TYPE, STRING_OBJECT_TYPE), NUMBER_TYPE.getLeastSupertype(STRING_OBJECT_TYPE)); assertTypeEquals(NUMBER_TYPE, NUMBER_TYPE.getLeastSupertype(NUMBER_TYPE)); assertTypeEquals(createUnionType(NUMBER_TYPE, functionType), NUMBER_TYPE.getLeastSupertype(functionType)); assertTypeEquals(createUnionType(NUMBER_TYPE, OBJECT_TYPE), NUMBER_TYPE.getLeastSupertype(OBJECT_TYPE)); assertTypeEquals(createUnionType(NUMBER_TYPE,

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>(STRING_OBJECT_TYPE)); assertFalse(NULL_TYPE.canAssignTo(NUMBER_TYPE)); assertFalse(NULL_TYPE.canAssignTo(functionType)); assertFalse(NULL_TYPE.canAssignTo(OBJECT_TYPE)); assertFalse(NULL_TYPE.canAssignTo(DATE_TYPE)); assertFalse(NULL_TYPE.canAssignTo(REGEXP_TYPE)); assertFalse(NULL_TYPE.canAssignTo(ARRAY_TYPE)); assertTrue(NULL_TYPE.canAssignTo(UNKNOWN_TYPE)); assertTrue(NULL_TYPE.canAssignTo(createNullableType(NO_OBJECT_TYPE))); assertTrue(NULL_TYPE.canAssignTo(createNullableType(NO_TYPE))); assertTrue(NULL_TYPE.canAssignTo(createNullableType(NULL_TYPE))); assertTrue(NULL_TYPE.canAssignTo(createNullableType(ALL_TYPE))); assertTrue(NULL_TYPE.canAssignTo(createNullableType(STRING_OBJECT_TYPE))); assertTrue(NULL_TYPE.canAssignTo(createNullableType(NUMBER_TYPE))); assertTrue(NULL_TYPE.canAssignTo(createNullableType(functionType))); assertTrue(NULL_TYPE.canAssignTo(createNullableType(OBJECT_TYPE))); assertTrue(NULL_TYPE.canAssignTo(createNullableType(DATE_TYPE))); assertTrue(NULL_TYPE.canAssignTo(createNullableType(REGEXP_TYPE))); assertTrue(NULL_TYPE.canAssignTo(createNullableType(ARRAY_TYPE))); // canBeCalled assertFalse(NULL_TYPE.canBeCalled()); // canTestForEqualityWith assertCanTestForEqualityWith(NULL_TYPE, NO_TYPE); assertCanTestForEqualityWith(NULL_TYPE, NO_OBJECT_TYPE); assertCanTestForEqualityWith(NULL_TYPE, ALL_TYPE); assertCannotTestForEqualityWith(NULL_TYPE, ARRAY_TYPE); assertCannotTestForEqualityWith(NULL_TYPE, BOOLEAN_TYPE); assertCannotTestForEqualityWith(NULL_TYPE, BOOLEAN_OBJECT_TYPE); assertCannotTestForEqualityWith(NULL_TYPE, DATE_TYPE); assertCannotTestForEqualityWith(NULL_TYPE, ERROR_TYPE); assertCannotTestForEqualityWith(NULL_TYPE, EVAL_ERROR_TYPE); assertCannotTestForEqualityWith(NULL_

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>TYPE.canAssignTo(REGEXP_TYPE)); assertFalse(STRING_OBJECT_TYPE.canAssignTo(ARRAY_TYPE)); assertFalse(STRING_OBJECT_TYPE.canAssignTo(STRING_TYPE)); // canBeCalled assertFalse(STRING_OBJECT_TYPE.canBeCalled()); // canTestForEqualityWith assertCanTestForEqualityWith(STRING_OBJECT_TYPE, ALL_TYPE); assertCanTestForEqualityWith(STRING_OBJECT_TYPE, STRING_OBJECT_TYPE); assertCanTestForEqualityWith(STRING_OBJECT_TYPE, STRING_TYPE); assertCanTestForEqualityWith(STRING_OBJECT_TYPE, functionType); assertCanTestForEqualityWith(STRING_OBJECT_TYPE, OBJECT_TYPE); assertCanTestForEqualityWith(STRING_OBJECT_TYPE, NUMBER_TYPE); assertCanTestForEqualityWith(STRING_OBJECT_TYPE, BOOLEAN_TYPE); assertCanTestForEqualityWith(STRING_OBJECT_TYPE, BOOLEAN_OBJECT_TYPE); assertCanTestForEqualityWith(STRING_OBJECT_TYPE, DATE_TYPE); assertCanTestForEqualityWith(STRING_OBJECT_TYPE, REGEXP_TYPE); assertCanTestForEqualityWith(STRING_OBJECT_TYPE, ARRAY_TYPE); assertCanTestForEqualityWith(STRING_OBJECT_TYPE, UNKNOWN_TYPE); // canTestForShallowEqualityWith assertTrue(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(NO_TYPE)); assertTrue(STRING_OBJECT_TYPE. canTestForShallowEqualityWith(NO_OBJECT_TYPE)); assertFalse(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(ARRAY_TYPE)); assertFalse(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(BOOLEAN_TYPE)); assertFalse(STRING_OBJECT_TYPE. canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); assertFalse(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(DATE_TYPE)); assertFalse(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(ERROR_TYPE)); assertFalse(STRING_OBJECT_TYPE. canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); assertFalse(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(functionType)); assertFalse(STRING_OBJECT_TYPE.canTestForSh

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>allowEqualityWith(NULL_TYPE)); assertFalse(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(NUMBER_TYPE)); assertFalse(STRING_OBJECT_TYPE. canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); assertTrue(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(OBJECT_TYPE)); assertFalse(STRING_OBJECT_TYPE. canTestForShallowEqualityWith(URI_ERROR_TYPE)); assertFalse(STRING_OBJECT_TYPE. canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); assertFalse(STRING_OBJECT_TYPE. canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); assertFalse(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(REGEXP_TYPE)); assertFalse(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(STRING_TYPE)); assertTrue(STRING_OBJECT_TYPE. canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); assertFalse(STRING_OBJECT_TYPE. canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); assertFalse(STRING_OBJECT_TYPE. canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); assertTrue(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(ALL_TYPE)); assertFalse(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(VOID_TYPE)); assertTrue(STRING_OBJECT_TYPE.canTestForShallowEqualityWith(UNKNOWN_TYPE)); // properties (ECMA-262 page 98 - 106) assertEquals(23, STRING_OBJECT_TYPE.getImplicitPrototype(). getPropertiesCount()); assertEquals(24, STRING_OBJECT_TYPE.getPropertiesCount()); assertReturnTypeEquals(STRING_TYPE, STRING_OBJECT_TYPE.getPropertyType("toString")); assertReturnTypeEquals(STRING_TYPE, STRING_OBJECT_TYPE.getPropertyType("valueOf")); assertReturnTypeEquals(STRING_TYPE, STRING_OBJECT_TYPE.getPropertyType("charAt")); assertReturnTypeEquals(NUMBER_TYPE, STRING_OBJECT_TYPE.getPropertyType("charCodeAt")); assertReturnTypeEquals(STRING_TYPE, STRING_OBJECT_TYPE.getPropertyType("concat")); assertReturnTypeEquals(NUMBER_TYPE, STRING_OBJECT_TYPE.getPropertyType("indexOf")); assert

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> assertFalse(STRING_TYPE.isBooleanObjectType()); assertFalse(STRING_TYPE.isBooleanValueType()); assertFalse(STRING_TYPE.isDateType()); assertFalse(STRING_TYPE.isEnumElementType()); assertFalse(STRING_TYPE.isNamedType()); assertFalse(STRING_TYPE.isNullType()); assertFalse(STRING_TYPE.isNumber()); assertFalse(STRING_TYPE.isNumberObjectType()); assertFalse(STRING_TYPE.isNumberValueType()); assertFalse(STRING_TYPE.isFunctionPrototypeType()); assertFalse(STRING_TYPE.isRegexpType()); assertTrue(STRING_TYPE.isString()); assertFalse(STRING_TYPE.isStringObjectType()); assertTrue(STRING_TYPE.isStringValueType()); assertFalse(STRING_TYPE.isEnumType()); assertFalse(STRING_TYPE.isUnionType()); assertFalse(STRING_TYPE.isAllType()); assertFalse(STRING_TYPE.isVoidType()); assertFalse(STRING_TYPE.isConstructor()); assertFalse(STRING_TYPE.isInstanceType()); // autoboxesTo assertTypeEquals(STRING_OBJECT_TYPE, STRING_TYPE.autoboxesTo()); // unboxesTo assertTypeEquals(STRING_TYPE, STRING_OBJECT_TYPE.unboxesTo()); // canAssignTo assertTrue(STRING_TYPE.canAssignTo(ALL_TYPE)); assertFalse(STRING_TYPE.canAssignTo(STRING_OBJECT_TYPE)); assertFalse(STRING_TYPE.canAssignTo(NUMBER_TYPE)); assertFalse(STRING_TYPE.canAssignTo(OBJECT_TYPE)); assertFalse(STRING_TYPE.canAssignTo(NUMBER_TYPE)); assertFalse(STRING_TYPE.canAssignTo(DATE_TYPE)); assertFalse(STRING_TYPE.canAssignTo(REGEXP_TYPE)); assertFalse(STRING_TYPE.canAssignTo(ARRAY_TYPE)); assertTrue(STRING_TYPE.canAssignTo(STRING_TYPE)); assertTrue(STRING_TYPE.canAssignTo(UNKNOWN_TYPE)); // canBeCalled assertFalse(STRING_TYPE.canBeCalled()); // canTestForEqualityWith assertCanTestForEqualityWith(STRING_TYPE, ALL_TYPE); assertCanTestForEqualityWith(STRING_TYPE, STRING_OBJECT_TYPE); assertCannotTestForEqualityWith(STRING_TYPE, functionType); assertCanTestForEqualityWith(STRING_TYPE, OBJECT_TYPE); assertCanTestFor

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>EqualityWith(STRING_TYPE, NUMBER_TYPE); assertCanTestForEqualityWith(STRING_TYPE, BOOLEAN_TYPE); assertCanTestForEqualityWith(STRING_TYPE, BOOLEAN_OBJECT_TYPE); assertCanTestForEqualityWith(STRING_TYPE, DATE_TYPE); assertCanTestForEqualityWith(STRING_TYPE, REGEXP_TYPE); assertCanTestForEqualityWith(STRING_TYPE, ARRAY_TYPE); assertCanTestForEqualityWith(STRING_TYPE, UNKNOWN_TYPE); // canTestForShallowEqualityWith assertTrue(STRING_TYPE.canTestForShallowEqualityWith(NO_TYPE)); assertFalse(STRING_TYPE.canTestForShallowEqualityWith(NO_OBJECT_TYPE)); assertFalse(STRING_TYPE.canTestForShallowEqualityWith(ARRAY_TYPE)); assertFalse(STRING_TYPE.canTestForShallowEqualityWith(BOOLEAN_TYPE)); assertFalse(STRING_TYPE.canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); assertFalse(STRING_TYPE.canTestForShallowEqualityWith(DATE_TYPE)); assertFalse(STRING_TYPE.canTestForShallowEqualityWith(ERROR_TYPE)); assertFalse(STRING_TYPE.canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); assertFalse(STRING_TYPE.canTestForShallowEqualityWith(functionType)); assertFalse(STRING_TYPE.canTestForShallowEqualityWith(NULL_TYPE)); assertFalse(STRING_TYPE.canTestForShallowEqualityWith(NUMBER_TYPE)); assertFalse(STRING_TYPE.canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); assertFalse(STRING_TYPE.canTestForShallowEqualityWith(OBJECT_TYPE)); assertFalse(STRING_TYPE.canTestForShallowEqualityWith(URI_ERROR_TYPE)); assertFalse(STRING_TYPE.canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); assertFalse(STRING_TYPE. canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); assertFalse(STRING_TYPE.canTestForShallowEqualityWith(REGEXP_TYPE)); assertTrue(STRING_TYPE.canTestForShallowEqualityWith(STRING_TYPE)); assertFalse(STRING_TYPE.canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); assertFalse(STRING_TYPE.canTestForShallow

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>EqualityWith(SYNTAX_ERROR_TYPE)); assertFalse(STRING_TYPE.canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); assertTrue(STRING_TYPE.canTestForShallowEqualityWith(ALL_TYPE)); assertFalse(STRING_TYPE.canTestForShallowEqualityWith(VOID_TYPE)); assertTrue(STRING_TYPE.canTestForShallowEqualityWith(UNKNOWN_TYPE)); // matchesXxx assertTrue(STRING_TYPE.matchesInt32Context()); assertTrue(STRING_TYPE.matchesNumberContext()); assertTrue(STRING_TYPE.matchesObjectContext()); assertTrue(STRING_TYPE.matchesStringContext()); assertTrue(STRING_TYPE.matchesUint32Context()); // isNullable assertFalse(STRING_TYPE.isNullable()); assertTrue(createNullableType(STRING_TYPE).isNullable()); // toString assertEquals("string", STRING_TYPE.toString()); assertTrue(STRING_TYPE.hasDisplayName()); assertEquals("string", STRING_TYPE.getDisplayName()); // findPropertyType assertTypeEquals(NUMBER_TYPE, STRING_TYPE.findPropertyType("length")); assertEquals(null, STRING_TYPE.findPropertyType("unknownProperty")); Asserts.assertResolvesToSame(STRING_TYPE); assertFalse(STRING_TYPE.isNominalConstructor()); } private void assertPropertyTypeDeclared(ObjectType ownerType, String prop) { assertTrue(ownerType.isPropertyTypeDeclared(prop)); assertFalse(ownerType.isPropertyTypeInferred(prop)); } private void assertPropertyTypeInferred(ObjectType ownerType, String prop) { assertFalse(ownerType.isPropertyTypeDeclared(prop)); assertTrue(ownerType.isPropertyTypeInferred(prop)); } private void assertPropertyTypeUnknown(ObjectType ownerType, String prop) { assertFalse(ownerType.isPropertyTypeDeclared(prop)); assertFalse(ownerType.isPropertyTypeInferred(prop)); assertTrue(ownerType.getPropertyType(prop).isUnknownType()); } private void assertReturnTypeEquals(JSType expectedReturnType, JSType function) { assertTrue(function instanceof FunctionType); assertTypeEquals(expectedReturnType, ((FunctionType) function).getReturnType()); } /** * Tests the behavior of record types. */ public void testRecordType() throws Exception { // isXxx assertTrue(recordType.isObject()); assertFalse(recordType.

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>isFunctionPrototypeType()); // canAssignTo assertTrue(recordType.canAssignTo(ALL_TYPE)); assertFalse(recordType.canAssignTo(STRING_OBJECT_TYPE)); assertFalse(recordType.canAssignTo(NUMBER_TYPE)); assertFalse(recordType.canAssignTo(DATE_TYPE)); assertFalse(recordType.canAssignTo(REGEXP_TYPE)); assertTrue(recordType.canAssignTo(UNKNOWN_TYPE)); assertTrue(recordType.canAssignTo(OBJECT_TYPE)); assertFalse(recordType.canAssignTo(U2U_CONSTRUCTOR_TYPE)); // autoboxesTo assertNull(recordType.autoboxesTo()); // canBeCalled assertFalse(recordType.canBeCalled()); // canTestForEqualityWith assertCanTestForEqualityWith(recordType, ALL_TYPE); assertCanTestForEqualityWith(recordType, STRING_OBJECT_TYPE); assertCanTestForEqualityWith(recordType, recordType); assertCanTestForEqualityWith(recordType, functionType); assertCanTestForEqualityWith(recordType, OBJECT_TYPE); assertCanTestForEqualityWith(recordType, NUMBER_TYPE); assertCanTestForEqualityWith(recordType, DATE_TYPE); assertCanTestForEqualityWith(recordType, REGEXP_TYPE); // canTestForShallowEqualityWith assertTrue(recordType.canTestForShallowEqualityWith(NO_TYPE)); assertTrue(recordType.canTestForShallowEqualityWith(NO_OBJECT_TYPE)); assertFalse(recordType.canTestForShallowEqualityWith(ARRAY_TYPE)); assertFalse(recordType.canTestForShallowEqualityWith(BOOLEAN_TYPE)); assertFalse(recordType. canTestForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); assertFalse(recordType.canTestForShallowEqualityWith(DATE_TYPE)); assertFalse(recordType.canTestForShallowEqualityWith(ERROR_TYPE)); assertFalse(recordType.canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); assertTrue(recordType.canTestForShallowEqualityWith(recordType)); assertFalse(recordType.canTestForShallowEqualityWith(NULL_TYPE)); assertFalse(recordType.canTestForShallowEqualityWith(NUMBER_TYPE)); assertFalse(recordType.canTestFor

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>ShallowEqualityWith(NUMBER_OBJECT_TYPE)); assertTrue(recordType.canTestForShallowEqualityWith(OBJECT_TYPE)); assertFalse(recordType.canTestForShallowEqualityWith(URI_ERROR_TYPE)); assertFalse(recordType.canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); assertFalse(recordType. canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); assertFalse(recordType.canTestForShallowEqualityWith(REGEXP_TYPE)); assertFalse(recordType.canTestForShallowEqualityWith(STRING_TYPE)); assertFalse(recordType.canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); assertFalse(recordType.canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); assertFalse(recordType.canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); assertTrue(recordType.canTestForShallowEqualityWith(ALL_TYPE)); assertFalse(recordType.canTestForShallowEqualityWith(VOID_TYPE)); assertTrue(recordType.canTestForShallowEqualityWith(UNKNOWN_TYPE)); // matchesXxx assertFalse(recordType.matchesInt32Context()); assertFalse(recordType.matchesNumberContext()); assertTrue(recordType.matchesObjectContext()); assertFalse(recordType.matchesStringContext()); assertFalse(recordType.matchesUint32Context()); Asserts.assertResolvesToSame(recordType); } /** * Tests the behavior of the instance of Function. */ public void testFunctionInstanceType() throws Exception { FunctionType functionInst = FUNCTION_INSTANCE_TYPE; // isXxx assertTrue(functionInst.isObject()); assertFalse(functionInst.isFunctionPrototypeType()); assertTrue(functionInst.getImplicitPrototype() .isFunctionPrototypeType()); // canAssignTo assertTrue(functionInst.canAssignTo(ALL_TYPE)); assertFalse(functionInst.canAssignTo(STRING_OBJECT_TYPE)); assertFalse(functionInst.canAssignTo(NUMBER_TYPE)); assertFalse(functionInst.canAssignTo(DATE_TYPE)); assertFalse(functionInst.canAssignTo(REGEXP_TYPE)); assertTrue(functionInst.canAssignTo(UNKNOWN_TYPE)); assertTrue(functionInst.canAssignTo(U2U_CONSTRUCTOR_TYPE)); // autoboxesTo

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> assertFalse(functionInst.canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); assertFalse(functionInst.canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); assertTrue(functionInst.canTestForShallowEqualityWith(ALL_TYPE)); assertFalse(functionInst.canTestForShallowEqualityWith(VOID_TYPE)); assertTrue(functionInst.canTestForShallowEqualityWith(UNKNOWN_TYPE)); // matchesXxx assertFalse(functionInst.matchesInt32Context()); assertFalse(functionInst.matchesNumberContext()); assertTrue(functionInst.matchesObjectContext()); assertFalse(functionInst.matchesStringContext()); assertFalse(functionInst.matchesUint32Context()); // hasProperty assertTrue(functionInst.hasProperty("prototype")); assertPropertyTypeInferred(functionInst, "prototype"); // misc assertTypeEquals(FUNCTION_FUNCTION_TYPE, functionInst.getConstructor()); assertTypeEquals(FUNCTION_PROTOTYPE, functionInst.getImplicitPrototype()); assertTypeEquals(functionInst, FUNCTION_FUNCTION_TYPE.getInstanceType()); Asserts.assertResolvesToSame(functionInst); } /** * Tests the behavior of functional types. */ public void testFunctionType() throws Exception { // isXxx assertTrue(functionType.isObject()); assertFalse(functionType.isFunctionPrototypeType()); assertTrue(functionType.getImplicitPrototype().getImplicitPrototype() .isFunctionPrototypeType()); // canAssignTo assertTrue(functionType.canAssignTo(ALL_TYPE)); assertFalse(functionType.canAssignTo(STRING_OBJECT_TYPE)); assertFalse(functionType.canAssignTo(NUMBER_TYPE)); assertFalse(functionType.canAssignTo(DATE_TYPE)); assertFalse(functionType.canAssignTo(REGEXP_TYPE)); assertTrue(functionType.canAssignTo(UNKNOWN_TYPE)); assertTrue(functionType.canAssignTo(U2U_CONSTRUCTOR_TYPE)); // autoboxesTo assertNull(functionType.autoboxesTo()); // canBeCalled assertTrue(functionType.canBeCalled()); // canTestForEqualityWith assertCanTestForEqualityWith(functionType, ALL_TYPE); assertCanTestForEqualityWith(functionType, STRING_OBJECT_TYPE); assertCanTestForEqualityWith(functionType, functionType);

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>(functionType.canTestForShallowEqualityWith(UNKNOWN_TYPE)); // matchesXxx assertFalse(functionType.matchesInt32Context()); assertFalse(functionType.matchesNumberContext()); assertTrue(functionType.matchesObjectContext()); assertFalse(functionType.matchesStringContext()); assertFalse(functionType.matchesUint32Context()); // hasProperty assertTrue(functionType.hasProperty("prototype")); assertPropertyTypeInferred(functionType, "prototype"); Asserts.assertResolvesToSame(functionType); assertEquals("aFunctionName", new FunctionBuilder(registry). withName("aFunctionName").build().getDisplayName()); } /** * Tests the subtyping relation of record types. */ public void testRecordTypeSubtyping() { RecordTypeBuilder builder = new RecordTypeBuilder(registry); builder.addProperty("a", NUMBER_TYPE, null); builder.addProperty("b", STRING_TYPE, null); builder.addProperty("c", STRING_TYPE, null); JSType subRecordType = builder.build(); assertTrue(subRecordType.isSubtype(recordType)); assertFalse(recordType.isSubtype(subRecordType)); builder = new RecordTypeBuilder(registry); builder.addProperty("a", OBJECT_TYPE, null); builder.addProperty("b", STRING_TYPE, null); JSType differentRecordType = builder.build(); assertFalse(differentRecordType.isSubtype(recordType)); assertFalse(recordType.isSubtype(differentRecordType)); } /** * Tests the subtyping relation of record types when an object has * an inferred property.. */ public void testRecordTypeSubtypingWithInferredProperties() { RecordTypeBuilder builder = new RecordTypeBuilder(registry); builder.addProperty("a", googSubBarInst, null); JSType record = builder.build(); ObjectType subtypeProp = registry.createAnonymousObjectType(); subtypeProp.defineInferredProperty("a", googSubSubBarInst, null); assertTrue(subtypeProp.isSubtype(record)); assertFalse(record.isSubtype(subtypeProp)); ObjectType supertypeProp = registry.createAnonymousObjectType(); supertypeProp.defineInferredProperty("a", googBarInst, null); assertFalse(supertypeProp.isSubtype(record)); assertFalse(record.isSubtype(supertypeProp

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> RecordTypeBuilder builder = new RecordTypeBuilder(registry); builder.addProperty("a", STRING_TYPE, null); JSType recordType = builder.build(); assertTypeEquals(NO_OBJECT_TYPE, recordType.getGreatestSubtype(U2U_CONSTRUCTOR_TYPE)); // if Function is given a property "a" of type "string", then it's // a subtype of the record type {a: string}. U2U_CONSTRUCTOR_TYPE.defineDeclaredProperty("a", STRING_TYPE, null); assertTypeEquals(U2U_CONSTRUCTOR_TYPE, recordType.getGreatestSubtype(U2U_CONSTRUCTOR_TYPE)); assertTypeEquals(U2U_CONSTRUCTOR_TYPE, U2U_CONSTRUCTOR_TYPE.getGreatestSubtype(recordType)); } public void testRecordTypeGreatestSubType6() { RecordTypeBuilder builder = new RecordTypeBuilder(registry); builder.addProperty("x", UNKNOWN_TYPE, null); JSType recordType = builder.build(); assertTypeEquals(NO_OBJECT_TYPE, recordType.getGreatestSubtype(U2U_CONSTRUCTOR_TYPE)); // if Function is given a property "x" of type "string", then it's // also a subtype of the record type {x: ?}. U2U_CONSTRUCTOR_TYPE.defineDeclaredProperty("x", STRING_TYPE, null); assertTypeEquals(U2U_CONSTRUCTOR_TYPE, recordType.getGreatestSubtype(U2U_CONSTRUCTOR_TYPE)); assertTypeEquals(U2U_CONSTRUCTOR_TYPE, U2U_CONSTRUCTOR_TYPE.getGreatestSubtype(recordType)); } public void testRecordTypeGreatestSubType7() { RecordTypeBuilder builder = new RecordTypeBuilder(registry); builder.addProperty("x", NUMBER_TYPE, null); JSType recordType = builder.build(); // if Function is given a property "x" of type "string", then it's // not a subtype of the record type {x: number}. U2U_CONSTRUCTOR_TYPE.defineDeclaredProperty("x", STRING_TYPE, null); assertTypeEquals(NO_OBJECT_TYPE, recordType.getGreatestSubtype(U2U

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>_CONSTRUCTOR_TYPE)); } public void testRecordTypeGreatestSubType8() { RecordTypeBuilder builder = new RecordTypeBuilder(registry); builder.addProperty("xyz", UNKNOWN_TYPE, null); JSType recordType = builder.build(); assertTypeEquals(NO_OBJECT_TYPE, recordType.getGreatestSubtype(U2U_CONSTRUCTOR_TYPE)); // if goog.Bar is given a property "xyz" of type "string", then it's // also a subtype of the record type {x: ?}. googBar.defineDeclaredProperty("xyz", STRING_TYPE, null); assertTypeEquals(googBar, recordType.getGreatestSubtype(U2U_CONSTRUCTOR_TYPE)); assertTypeEquals(googBar, U2U_CONSTRUCTOR_TYPE.getGreatestSubtype(recordType)); ObjectType googBarInst = googBar.getInstanceType(); assertTypeEquals(NO_OBJECT_TYPE, recordType.getGreatestSubtype(googBarInst)); assertTypeEquals(NO_OBJECT_TYPE, googBarInst.getGreatestSubtype(recordType)); } /** * Tests the "apply" method on the function type. */ public void testApplyOfDateMethod() { JSType applyType = dateMethod.getPropertyType("apply"); assertTrue("apply should be a function", applyType instanceof FunctionType); FunctionType applyFn = (FunctionType) applyType; assertTypeEquals("apply should have the same return type as its function", NUMBER_TYPE, applyFn.getReturnType()); Node params = applyFn.getParametersNode(); assertEquals("apply takes two args", 2, params.getChildCount()); assertTypeEquals("apply's first arg is the @this type", registry.createOptionalNullableType(DATE_TYPE), params.getFirstChild().getJSType()); assertTypeEquals("apply's second arg is an Array", registry.createOptionalNullableType(OBJECT_TYPE), params.getLastChild().getJSType()); assertTrue("apply's args must be optional", params.getFirstChild().isOptionalArg()); assertTrue("apply's args must be optional", params.getLastChild().isOptionalArg()); } /** * Tests the "call" method on the function type. */ public void testCallOf

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>isAllType()); assertFalse(BOOLEAN_TYPE.isVoidType()); assertFalse(BOOLEAN_TYPE.isConstructor()); assertFalse(BOOLEAN_TYPE.isInstanceType()); // autoboxesTo assertTypeEquals(BOOLEAN_OBJECT_TYPE, BOOLEAN_TYPE.autoboxesTo()); // unboxesTo assertTypeEquals(BOOLEAN_TYPE, BOOLEAN_OBJECT_TYPE.unboxesTo()); // canAssignTo assertTrue(BOOLEAN_TYPE.canAssignTo(ALL_TYPE)); assertFalse(BOOLEAN_TYPE.canAssignTo(STRING_OBJECT_TYPE)); assertFalse(BOOLEAN_TYPE.canAssignTo(NUMBER_TYPE)); assertFalse(BOOLEAN_TYPE.canAssignTo(functionType)); assertFalse(BOOLEAN_TYPE.canAssignTo(NULL_TYPE)); assertFalse(BOOLEAN_TYPE.canAssignTo(OBJECT_TYPE)); assertFalse(BOOLEAN_TYPE.canAssignTo(DATE_TYPE)); assertTrue(BOOLEAN_TYPE.canAssignTo(unresolvedNamedType)); assertFalse(BOOLEAN_TYPE.canAssignTo(namedGoogBar)); assertFalse(BOOLEAN_TYPE.canAssignTo(REGEXP_TYPE)); // canBeCalled assertFalse(BOOLEAN_TYPE.canBeCalled()); // canTestForEqualityWith assertCanTestForEqualityWith(BOOLEAN_TYPE, ALL_TYPE); assertCanTestForEqualityWith(BOOLEAN_TYPE, STRING_OBJECT_TYPE); assertCanTestForEqualityWith(BOOLEAN_TYPE, NUMBER_TYPE); assertCannotTestForEqualityWith(BOOLEAN_TYPE, functionType); assertCannotTestForEqualityWith(BOOLEAN_TYPE, VOID_TYPE); assertCanTestForEqualityWith(BOOLEAN_TYPE, OBJECT_TYPE); assertCanTestForEqualityWith(BOOLEAN_TYPE, DATE_TYPE); assertCanTestForEqualityWith(BOOLEAN_TYPE, REGEXP_TYPE); assertCanTestForEqualityWith(BOOLEAN_TYPE, UNKNOWN_TYPE); // canTestForShallowEqualityWith assertTrue(BOOLEAN_TYPE.canTestForShallowEqualityWith(NO_TYPE)); assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(NO_OBJECT_TYPE)); assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(ARRAY_TYPE)); assertTrue(BOOLEAN_TYPE.canTestForShallowEqualityWith(BOOLEAN_TYPE)); assertFalse(BOOLEAN_TYPE. canTest

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>ForShallowEqualityWith(BOOLEAN_OBJECT_TYPE)); assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(DATE_TYPE)); assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(ERROR_TYPE)); assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(EVAL_ERROR_TYPE)); assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(functionType)); assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(NULL_TYPE)); assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(NUMBER_TYPE)); assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(NUMBER_OBJECT_TYPE)); assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(OBJECT_TYPE)); assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(URI_ERROR_TYPE)); assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(RANGE_ERROR_TYPE)); assertFalse(BOOLEAN_TYPE. canTestForShallowEqualityWith(REFERENCE_ERROR_TYPE)); assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(REGEXP_TYPE)); assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(STRING_TYPE)); assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(STRING_OBJECT_TYPE)); assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(SYNTAX_ERROR_TYPE)); assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(TYPE_ERROR_TYPE)); assertTrue(BOOLEAN_TYPE.canTestForShallowEqualityWith(ALL_TYPE)); assertFalse(BOOLEAN_TYPE.canTestForShallowEqualityWith(VOID_TYPE)); assertTrue(BOOLEAN_TYPE.canTestForShallowEqualityWith(UNKNOWN_TYPE)); // isNullable assertFalse(BOOLEAN_TYPE.isNullable()); // matchesXxx assertTrue(BOOLEAN_TYPE.matchesInt32Context()); assertTrue(BOOLEAN_TYPE.matchesNumberContext()); assertTrue(BOOLEAN_TYPE.matchesObjectContext()); assertTrue(BOOLEAN_TYPE.matchesStringContext()); assertTrue(BOOLEAN_TYPE.matchesUint32Context()); // toString assertEquals("boolean", BOOLEAN_TYPE.toString()); assertTrue(BOOLEAN_TYPE.hasDisplayName()); assertEquals("boolean", BOOLEAN_TYPE.getDisplayName()); Asserts.

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> elementsType.toString()); assertTrue(elementsType.hasDisplayName()); assertEquals("Enum", elementsType.getDisplayName()); Asserts.assertResolvesToSame(elementsType); } public void testStringEnumType() throws Exception { EnumElementType stringEnum = new EnumType(registry, "Enum", null, STRING_TYPE).getElementsType(); assertTypeEquals(UNKNOWN_TYPE, stringEnum.getPropertyType("length")); assertTypeEquals(NUMBER_TYPE, stringEnum.findPropertyType("length")); assertEquals(false, stringEnum.hasProperty("length")); assertTypeEquals(STRING_OBJECT_TYPE, stringEnum.autoboxesTo()); assertNull(stringEnum.getConstructor()); Asserts.assertResolvesToSame(stringEnum); } public void testStringObjectEnumType() throws Exception { EnumElementType stringEnum = new EnumType(registry, "Enum", null, STRING_OBJECT_TYPE) .getElementsType(); assertTypeEquals(NUMBER_TYPE, stringEnum.getPropertyType("length")); assertTypeEquals(NUMBER_TYPE, stringEnum.findPropertyType("length")); assertEquals(true, stringEnum.hasProperty("length")); assertTypeEquals(STRING_OBJECT_FUNCTION_TYPE, stringEnum.getConstructor()); } /** * Tests object types. */ public void testObjectType() throws Exception { PrototypeObjectType objectType = new PrototypeObjectType(registry, null, null); // isXxx assertFalse(objectType.isAllType()); assertFalse(objectType.isArrayType()); assertFalse(objectType.isDateType()); assertFalse(objectType.isFunctionPrototypeType()); assertTrue(objectType.getImplicitPrototype() == OBJECT_TYPE); // canAssignTo assertTrue(objectType.canAssignTo(ALL_TYPE)); assertFalse(objectType.canAssignTo(STRING_OBJECT_TYPE)); assertFalse(objectType.canAssignTo(NUMBER_TYPE)); assertFalse(objectType.canAssignTo(functionType)); assertFalse(objectType.canAssignTo(NULL_TYPE)); assertFalse(objectType.canAssignTo(DATE_TYPE)); assertTrue(objectType.canAssignTo(OBJECT_TYPE)); assertTrue(objectType.canAssignTo(unresolvedNamedType)); assertFalse(objectType.canAssignTo(namedGoogBar)); assertFalse(object

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> URIError assertTypeEquals( URI_ERROR_FUNCTION_TYPE, URI_ERROR_TYPE.getConstructor()); } /** * Tests that the method {@link JSType#canTestForEqualityWith(JSType)} handles * special corner cases. */ @SuppressWarnings("checked") public void testCanTestForEqualityWithCornerCases() { // null == undefined is always true assertCannotTestForEqualityWith(NULL_TYPE, VOID_TYPE); // (Object,null) == undefined could be true or false UnionType nullableObject = (UnionType) createUnionType(OBJECT_TYPE, NULL_TYPE); assertCanTestForEqualityWith(nullableObject, VOID_TYPE); assertCanTestForEqualityWith(VOID_TYPE, nullableObject); } /** * Tests the {@link JSType#testForEquality(JSType)} method. */ public void testTestForEquality() { compare(TRUE, NO_OBJECT_TYPE, NO_OBJECT_TYPE); compare(UNKNOWN, ALL_TYPE, ALL_TYPE); compare(TRUE, NO_TYPE, NO_TYPE); compare(UNKNOWN, NO_RESOLVED_TYPE, NO_RESOLVED_TYPE); compare(UNKNOWN, NO_OBJECT_TYPE, NUMBER_TYPE); compare(UNKNOWN, ALL_TYPE, NUMBER_TYPE); compare(UNKNOWN, NO_TYPE, NUMBER_TYPE); compare(FALSE, NULL_TYPE, BOOLEAN_TYPE); compare(TRUE, NULL_TYPE, NULL_TYPE); compare(FALSE, NULL_TYPE, NUMBER_TYPE); compare(FALSE, NULL_TYPE, OBJECT_TYPE); compare(FALSE, NULL_TYPE, STRING_TYPE); compare(TRUE, NULL_TYPE, VOID_TYPE); compare(UNKNOWN, NULL_TYPE, createUnionType(UNKNOWN_TYPE, VOID_TYPE)); compare(UNKNOWN, NULL_TYPE, createUnionType(OBJECT_TYPE, VOID_TYPE)); compare(UNKNOWN, NULL_TYPE, unresolvedNamedType); compare(UNKNOWN, NULL_TYPE, createUnionType(unresolvedNamedType, DATE_TYPE)); compare(FALSE, VOID_TYPE, REGEXP_TYPE); compare(TRUE, VOID_TYPE, VOID_TYPE); compare(UNKNOWN, VOID_TYPE, createUnionType(REGEXP_TYPE, VOID_TYPE

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>)); compare(UNKNOWN, NUMBER_TYPE, BOOLEAN_TYPE); compare(UNKNOWN, NUMBER_TYPE, NUMBER_TYPE); compare(UNKNOWN, NUMBER_TYPE, OBJECT_TYPE); compare(UNKNOWN, ARRAY_TYPE, BOOLEAN_TYPE); compare(UNKNOWN, OBJECT_TYPE, BOOLEAN_TYPE); compare(UNKNOWN, OBJECT_TYPE, STRING_TYPE); compare(UNKNOWN, STRING_TYPE, STRING_TYPE); compare(UNKNOWN, STRING_TYPE, BOOLEAN_TYPE); compare(UNKNOWN, STRING_TYPE, NUMBER_TYPE); compare(FALSE, STRING_TYPE, VOID_TYPE); compare(FALSE, STRING_TYPE, NULL_TYPE); compare(FALSE, STRING_TYPE, createUnionType(NULL_TYPE, VOID_TYPE)); compare(UNKNOWN, UNKNOWN_TYPE, BOOLEAN_TYPE); compare(UNKNOWN, UNKNOWN_TYPE, NULL_TYPE); compare(UNKNOWN, UNKNOWN_TYPE, VOID_TYPE); compare(FALSE, U2U_CONSTRUCTOR_TYPE, BOOLEAN_TYPE); compare(FALSE, U2U_CONSTRUCTOR_TYPE, NUMBER_TYPE); compare(FALSE, U2U_CONSTRUCTOR_TYPE, STRING_TYPE); compare(FALSE, U2U_CONSTRUCTOR_TYPE, VOID_TYPE); compare(FALSE, U2U_CONSTRUCTOR_TYPE, NULL_TYPE); compare(UNKNOWN, U2U_CONSTRUCTOR_TYPE, OBJECT_TYPE); compare(UNKNOWN, U2U_CONSTRUCTOR_TYPE, ALL_TYPE); compare(UNKNOWN, NULL_TYPE, subclassOfUnresolvedNamedType); JSType functionAndNull = createUnionType(NULL_TYPE, dateMethod); compare(UNKNOWN, functionAndNull, dateMethod); compare(UNKNOWN, NULL_TYPE, NO_TYPE); compare(UNKNOWN, VOID_TYPE, NO_TYPE); compare(UNKNOWN, NULL_TYPE, unresolvedNamedType); compare(UNKNOWN, VOID_TYPE, unresolvedNamedType); compare(TRUE, NO_TYPE, NO_TYPE); } private void compare(TernaryValue r, JSType t1, JSType t2) { assertEquals(r, t1.testForEquality(t2)); assertEquals(r, t2.testForEquality(t1)); } private void assertCanTestForEqualityWith(JS

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> assertTrue(U2U_CONSTRUCTOR_TYPE.isSubtype(f1)); assertTrue(U2U_CONSTRUCTOR_TYPE.isSubtype(f2)); } public void testSubtypingFunctionFixedArgsNotMatching() throws Exception { FunctionType f1 = registry.createFunctionType(OBJECT_TYPE, false, EVAL_ERROR_TYPE, UNKNOWN_TYPE); FunctionType f2 = registry.createFunctionType(STRING_OBJECT_TYPE, false, ERROR_TYPE, ALL_TYPE); assertTrue(f1.isSubtype(f1)); assertFalse(f1.isSubtype(f2)); assertTrue(f2.isSubtype(f1)); assertTrue(f2.isSubtype(f2)); assertTrue(f1.isSubtype(U2U_CONSTRUCTOR_TYPE)); assertTrue(f2.isSubtype(U2U_CONSTRUCTOR_TYPE)); assertTrue(U2U_CONSTRUCTOR_TYPE.isSubtype(f1)); assertTrue(U2U_CONSTRUCTOR_TYPE.isSubtype(f2)); } public void testSubtypingFunctionVariableArgsOneOnly() throws Exception { // f1 = (EvalError...) -> Object FunctionType f1 = registry.createFunctionType(OBJECT_TYPE, true, EVAL_ERROR_TYPE); // f2 = (Error, Object) -> String FunctionType f2 = registry.createFunctionType(STRING_OBJECT_TYPE, false, ERROR_TYPE, OBJECT_TYPE); assertTrue(f1.isSubtype(f1)); assertFalse(f1.isSubtype(f2)); assertFalse(f2.isSubtype(f1)); assertTrue(f2.isSubtype(f2)); assertTrue(f1.isSubtype(U2U_CONSTRUCTOR_TYPE)); assertTrue(f2.isSubtype(U2U_CONSTRUCTOR_TYPE)); assertTrue(U2U_CONSTRUCTOR_TYPE.isSubtype(f1)); assertTrue(U2U_CONSTRUCTOR_TYPE.isSubtype(f2)); } public void testSubtypingFunctionVariableArgsBoth() throws Exception { // f1 = (UriError, EvalError, EvalError...) -> Object FunctionType f1 = registry.createFunctionType(OBJECT_TYPE, true, URI_ERROR_TYPE, EVAL_ERROR_TYPE, E

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>7)); assertTrue(LEAST_FUNCTION_TYPE.isSubtype(f8)); assertFalse(GREATEST_FUNCTION_TYPE.isSubtype(f1)); assertFalse(GREATEST_FUNCTION_TYPE.isSubtype(f2)); assertFalse(GREATEST_FUNCTION_TYPE.isSubtype(f3)); assertFalse(GREATEST_FUNCTION_TYPE.isSubtype(f4)); assertFalse(GREATEST_FUNCTION_TYPE.isSubtype(f5)); assertFalse(GREATEST_FUNCTION_TYPE.isSubtype(f6)); assertFalse(GREATEST_FUNCTION_TYPE.isSubtype(f7)); assertFalse(GREATEST_FUNCTION_TYPE.isSubtype(f8)); } /** * Types to test for symmetrical relationships. */ private List<JSType> getTypesToTestForSymmetry() { return Lists.newArrayList( UNKNOWN_TYPE, NULL_TYPE, VOID_TYPE, NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE, OBJECT_TYPE, U2U_CONSTRUCTOR_TYPE, LEAST_FUNCTION_TYPE, GREATEST_FUNCTION_TYPE, ALL_TYPE, NO_TYPE, NO_OBJECT_TYPE, NO_RESOLVED_TYPE, createUnionType(BOOLEAN_TYPE, STRING_TYPE), createUnionType(NUMBER_TYPE, STRING_TYPE), createUnionType(NULL_TYPE, dateMethod), createUnionType(UNKNOWN_TYPE, dateMethod), createUnionType(namedGoogBar, dateMethod), createUnionType(NULL_TYPE, unresolvedNamedType), enumType, elementsType, dateMethod, functionType, unresolvedNamedType, googBar, namedGoogBar, googBar.getInstanceType(), namedGoogBar, subclassOfUnresolvedNamedType, subclassCtor, recordType, forwardDeclaredNamedType, createUnionType(forwardDeclaredNamedType, NULL_TYPE)); } public void testSymmetryOfTestForEquality() { List<JSType> listA = getTypesToTestForSymmetry(); List<JSType> listB = getTypesToTestForSymmetry(); for (JSType typeA : listA) { for (JSType typeB : listB

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>)", expected.toString()); } public void testLeastSupertypeUnresolvedNamedType2() { JSType expected = registry.createUnionType( unresolvedNamedType, UNKNOWN_TYPE); assertTypeEquals(expected, unresolvedNamedType.getLeastSupertype(UNKNOWN_TYPE)); assertTypeEquals(expected, UNKNOWN_TYPE.getLeastSupertype(unresolvedNamedType)); assertTypeEquals(UNKNOWN_TYPE, expected); } public void testLeastSupertypeUnresolvedNamedType3() { JSType expected = registry.createUnionType( unresolvedNamedType, CHECKED_UNKNOWN_TYPE); assertTypeEquals(expected, unresolvedNamedType.getLeastSupertype(CHECKED_UNKNOWN_TYPE)); assertTypeEquals(expected, CHECKED_UNKNOWN_TYPE.getLeastSupertype(unresolvedNamedType)); assertTypeEquals(CHECKED_UNKNOWN_TYPE, expected); } /** Tests the subclass of an unresolved named type */ public void testSubclassOfUnresolvedNamedType() { assertTrue(subclassOfUnresolvedNamedType.isUnknownType()); } /** * Tests that Proxied FunctionTypes behave the same over getLeastSupertype and * getGreatestSubtype as non proxied FunctionTypes */ public void testSupertypeOfProxiedFunctionTypes() { ObjectType fn1 = new FunctionBuilder(registry) .withParamsNode(new Node(Token.PARAM_LIST)) .withReturnType(NUMBER_TYPE) .build(); ObjectType fn2 = new FunctionBuilder(registry) .withParamsNode(new Node(Token.PARAM_LIST)) .withReturnType(STRING_TYPE) .build(); ObjectType p1 = new ProxyObjectType(registry, fn1); ObjectType p2 = new ProxyObjectType(registry, fn2); ObjectType supremum = new FunctionBuilder(registry) .withParamsNode(new Node(Token.PARAM_LIST)) .withReturnType(registry.createUnionType(STRING_TYPE, NUMBER_TYPE)) .build(); assertTypeEquals(fn1.getLeastSupertype(fn2), p1.getLeastSupertype(p2)); assertTypeEquals(supremum, fn1.getLeastSupertype(fn2)); assertTypeEquals(supremum, fn1.

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>(a.isResolved()); assertTrue(b.isResolved()); assertEquals(a.hashCode(), realA.hashCode()); assertTypeEquals(a, realA); assertEquals(b.hashCode(), realB.hashCode()); assertTypeEquals(b, realB); JSType resolvedA = Asserts.assertValidResolve(a); assertNotSame(resolvedA, a); assertSame(resolvedA, realA); JSType resolvedB = Asserts.assertValidResolve(b); assertNotSame(resolvedB, b); assertSame(resolvedB, realB); } /** * Tests the {@link NamedType#equals} function against other types * when it's forward-declared. */ public void testForwardDeclaredNamedTypeEquals() { // test == if references are equal NamedType a = new NamedType(registry, "typeA", "source", 1, 0); NamedType b = new NamedType(registry, "typeA", "source", 1, 0); registry.forwardDeclareType("typeA"); assertEquals(a.hashCode(), b.hashCode()); assertTypeEquals(a, b); a.resolve(null, EMPTY_SCOPE); assertTrue(a.isResolved()); assertFalse(b.isResolved()); assertEquals(a.hashCode(), b.hashCode()); assertTypeEquals(a, b); assertFalse(a.isEquivalentTo(UNKNOWN_TYPE)); assertFalse(b.isEquivalentTo(UNKNOWN_TYPE)); assertTrue(a.isEmptyType()); assertFalse(a.isNoType()); assertTrue(a.isNoResolvedType()); } public void testForwardDeclaredNamedType() { NamedType a = new NamedType(registry, "typeA", "source", 1, 0); registry.forwardDeclareType("typeA"); assertTypeEquals(UNKNOWN_TYPE, a.getLeastSupertype(UNKNOWN_TYPE)); assertTypeEquals(CHECKED_UNKNOWN_TYPE, a.getLeastSupertype(CHECKED_UNKNOWN_TYPE)); assertTypeEquals(UNKNOWN_TYPE, UNKNOWN_TYPE.getLeastSupertype(a)); assertTypeEquals(CHECKED_UNKNOWN_TYPE, CHECKED_UNKNOWN_TYPE.getLeastSupertype(a)); } /** * Tests {@link JSType#getGreatestSubtype

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>(JSType)} on simple types. */ public void testGreatestSubtypeSimpleTypes() { assertTypeEquals(ARRAY_TYPE, ARRAY_TYPE.getGreatestSubtype(ALL_TYPE)); assertTypeEquals(ARRAY_TYPE, ALL_TYPE.getGreatestSubtype(ARRAY_TYPE)); assertTypeEquals(NO_OBJECT_TYPE, REGEXP_TYPE.getGreatestSubtype(NO_OBJECT_TYPE)); assertTypeEquals(NO_OBJECT_TYPE, NO_OBJECT_TYPE.getGreatestSubtype(REGEXP_TYPE)); assertTypeEquals(NO_OBJECT_TYPE, ARRAY_TYPE.getGreatestSubtype(STRING_OBJECT_TYPE)); assertTypeEquals(NO_TYPE, ARRAY_TYPE.getGreatestSubtype(NUMBER_TYPE)); assertTypeEquals(NO_OBJECT_TYPE, ARRAY_TYPE.getGreatestSubtype(functionType)); assertTypeEquals(STRING_OBJECT_TYPE, STRING_OBJECT_TYPE.getGreatestSubtype(OBJECT_TYPE)); assertTypeEquals(STRING_OBJECT_TYPE, OBJECT_TYPE.getGreatestSubtype(STRING_OBJECT_TYPE)); assertTypeEquals(NO_OBJECT_TYPE, ARRAY_TYPE.getGreatestSubtype(DATE_TYPE)); assertTypeEquals(NO_OBJECT_TYPE, ARRAY_TYPE.getGreatestSubtype(REGEXP_TYPE)); assertTypeEquals(EVAL_ERROR_TYPE, ERROR_TYPE.getGreatestSubtype(EVAL_ERROR_TYPE)); assertTypeEquals(EVAL_ERROR_TYPE, EVAL_ERROR_TYPE.getGreatestSubtype(ERROR_TYPE)); assertTypeEquals(NO_TYPE, NULL_TYPE.getGreatestSubtype(ERROR_TYPE)); assertTypeEquals(UNKNOWN_TYPE, NUMBER_TYPE.getGreatestSubtype(UNKNOWN_TYPE)); assertTypeEquals(NO_RESOLVED_TYPE, NO_OBJECT_TYPE.getGreatestSubtype(forwardDeclaredNamedType)); assertTypeEquals(NO_RESOLVED_TYPE, forwardDeclaredNamedType.getGreatestSubtype(NO_OBJECT_TYPE)); } /** * Tests that a derived class extending a type via a named type is a subtype * of it. */ public void testSubtypingDerivedExtendsNamedBaseType() throws Exception { ObjectType derived = registry

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>NativeType(JSTypeNative.NO_OBJECT_TYPE), registry.getNativeType(JSTypeNative.NO_TYPE)); verifySubtypeChain(typeChain); } public void testRecordAndObjectChain2() throws Exception { RecordTypeBuilder builder = new RecordTypeBuilder(registry); builder.addProperty("date", DATE_TYPE, null); JSType hasDateProperty = builder.build(); List<JSType> typeChain = Lists.newArrayList( registry.getNativeType(JSTypeNative.OBJECT_TYPE), hasDateProperty, googBar.getInstanceType(), registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), registry.getNativeType(JSTypeNative.NO_TYPE)); verifySubtypeChain(typeChain); } public void testRecordAndObjectChain3() throws Exception { RecordTypeBuilder builder = new RecordTypeBuilder(registry); builder.addProperty("date", UNKNOWN_TYPE, null); JSType hasUnknownDateProperty = builder.build(); List<JSType> typeChain = Lists.newArrayList( registry.getNativeType(JSTypeNative.OBJECT_TYPE), hasUnknownDateProperty, googBar.getInstanceType(), registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), registry.getNativeType(JSTypeNative.NO_TYPE)); verifySubtypeChain(typeChain); } public void testNullableNamedTypeChain() throws Exception { List<JSType> typeChain = Lists.newArrayList( registry.getNativeType(JSTypeNative.ALL_TYPE), registry.createOptionalNullableType( registry.getNativeType(JSTypeNative.OBJECT_PROTOTYPE)), registry.createOptionalNullableType( registry.getNativeType(JSTypeNative.OBJECT_TYPE)), registry.createOptionalNullableType(googBar.getPrototype()), registry.createOptionalNullableType(googBar.getInstanceType()), registry.createNullableType(googSubBar.getPrototype()), registry.createNullableType(googSubBar.getInstanceType()), googSubSubBar.getPrototype(), googSubSubBar.getInstanceType(), registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), registry.getNativeType(JSTypeNative.NO_TYPE)); verifySubtypeChain(typeChain); }

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>RestrictedTypeGivenToBooleanOutcome(true)); assertTypeEquals(BOOLEAN_TYPE, BOOLEAN_TYPE.getRestrictedTypeGivenToBooleanOutcome(false)); assertTypeEquals(NO_TYPE, NULL_TYPE.getRestrictedTypeGivenToBooleanOutcome(true)); assertTypeEquals(NULL_TYPE, NULL_TYPE.getRestrictedTypeGivenToBooleanOutcome(false)); assertTypeEquals(NUMBER_TYPE, NUMBER_TYPE.getRestrictedTypeGivenToBooleanOutcome(true)); assertTypeEquals(NUMBER_TYPE, NUMBER_TYPE.getRestrictedTypeGivenToBooleanOutcome(false)); assertTypeEquals(STRING_TYPE, STRING_TYPE.getRestrictedTypeGivenToBooleanOutcome(true)); assertTypeEquals(STRING_TYPE, STRING_TYPE.getRestrictedTypeGivenToBooleanOutcome(false)); assertTypeEquals(STRING_OBJECT_TYPE, STRING_OBJECT_TYPE.getRestrictedTypeGivenToBooleanOutcome(true)); assertTypeEquals(NO_TYPE, STRING_OBJECT_TYPE.getRestrictedTypeGivenToBooleanOutcome(false)); assertTypeEquals(NO_TYPE, VOID_TYPE.getRestrictedTypeGivenToBooleanOutcome(true)); assertTypeEquals(VOID_TYPE, VOID_TYPE.getRestrictedTypeGivenToBooleanOutcome(false)); assertTypeEquals(NO_OBJECT_TYPE, NO_OBJECT_TYPE.getRestrictedTypeGivenToBooleanOutcome(true)); assertTypeEquals(NO_TYPE, NO_OBJECT_TYPE.getRestrictedTypeGivenToBooleanOutcome(false)); assertTypeEquals(NO_TYPE, NO_TYPE.getRestrictedTypeGivenToBooleanOutcome(true)); assertTypeEquals(NO_TYPE, NO_TYPE.getRestrictedTypeGivenToBooleanOutcome(false)); assertTypeEquals(ALL_TYPE, ALL_TYPE.getRestrictedTypeGivenToBooleanOutcome(true)); assertTypeEquals(ALL_TYPE, ALL_TYPE.getRestrictedTypeGivenToBooleanOutcome(false)); assertTypeEquals(CHECKED_UNKNOWN_TYPE, UNKNOWN_TYPE.getRestrictedTypeGivenToBooleanOutcome(true)); assertTypeEquals(UNKNOWN_TYPE, UNKNOWN_TYPE.getRestrictedTypeGivenToBooleanOutcome(false)); // unions UnionType nullableStringValue = (UnionType) createNullableType(STRING_TYPE); assertTypeEquals(STRING

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>_TYPE, nullableStringValue.getRestrictedTypeGivenToBooleanOutcome(true)); assertTypeEquals(nullableStringValue, nullableStringValue.getRestrictedTypeGivenToBooleanOutcome(false)); UnionType nullableStringObject = (UnionType) createNullableType(STRING_OBJECT_TYPE); assertTypeEquals(STRING_OBJECT_TYPE, nullableStringObject.getRestrictedTypeGivenToBooleanOutcome(true)); assertTypeEquals(NULL_TYPE, nullableStringObject.getRestrictedTypeGivenToBooleanOutcome(false)); } public void testRegisterProperty() { int i = 0; List<JSType> allObjects = Lists.newArrayList(); for (JSType type : types) { String propName = "ALF" + i++; if (type instanceof ObjectType) { ObjectType objType = (ObjectType) type; objType.defineDeclaredProperty(propName, UNKNOWN_TYPE, null); objType.defineDeclaredProperty("allHaz", UNKNOWN_TYPE, null); assertTypeEquals(type, registry.getGreatestSubtypeWithProperty(type, propName)); List<JSType> typesWithProp = Lists.newArrayList(registry.getTypesWithProperty(propName)); String message = type.toString(); assertEquals(message, 1, typesWithProp.size()); assertTypeEquals(type, typesWithProp.get(0)); assertTypeEquals(NO_TYPE, registry.getGreatestSubtypeWithProperty(type, "GRRR")); allObjects.add(type); } } assertTypeListEquals(registry.getTypesWithProperty("GRRR"), Lists.newArrayList(NO_TYPE)); assertTypeListEquals(allObjects, registry.getTypesWithProperty("allHaz")); } public void testRegisterPropertyMemoization() { ObjectType derived1 = registry.createObjectType("d1", null, namedGoogBar); ObjectType derived2 = registry.createObjectType("d2", null, namedGoogBar); derived1.defineDeclaredProperty("propz", UNKNOWN_TYPE, null); assertTypeEquals(derived1, registry.getGreatestSubtypeWithProperty(derived1, "propz")); assertTypeEquals(NO_OBJECT_TYPE, registry.getGreatestSubtypeWithProperty(derived2, "propz")); derived2.

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>defineDeclaredProperty("propz", UNKNOWN_TYPE, null); assertTypeEquals(derived1, registry.getGreatestSubtypeWithProperty(derived1, "propz")); assertTypeEquals(derived2, registry.getGreatestSubtypeWithProperty(derived2, "propz")); } /** * Tests * {@link JSTypeRegistry#getGreatestSubtypeWithProperty(JSType, String)}. */ public void testGreatestSubtypeWithProperty() { ObjectType foo = registry.createObjectType("foo", null, OBJECT_TYPE); ObjectType bar = registry.createObjectType("bar", null, namedGoogBar); foo.defineDeclaredProperty("propz", UNKNOWN_TYPE, null); bar.defineDeclaredProperty("propz", UNKNOWN_TYPE, null); assertTypeEquals(bar, registry.getGreatestSubtypeWithProperty(namedGoogBar, "propz")); } public void testGoodSetPrototypeBasedOn() { FunctionType fun = registry.createConstructorType("fun", null, null, null); fun.setPrototypeBasedOn(unresolvedNamedType); assertTrue(fun.getInstanceType().isUnknownType()); } public void testLateSetPrototypeBasedOn() { FunctionType fun = registry.createConstructorType("fun", null, null, null); assertFalse(fun.getInstanceType().isUnknownType()); fun.setPrototypeBasedOn(unresolvedNamedType); assertTrue(fun.getInstanceType().isUnknownType()); } public void testGetTypeUnderEquality1() { for (JSType type : types) { testGetTypeUnderEquality(type, type, type, type); } } public void testGetTypesUnderEquality2() { // objects can be equal to numbers testGetTypeUnderEquality( NUMBER_TYPE, OBJECT_TYPE, NUMBER_TYPE, OBJECT_TYPE); } public void testGetTypesUnderEquality3() { // null == undefined testGetTypeUnderEquality( NULL_TYPE, VOID_TYPE, NULL_TYPE, VOID_TYPE); } @SuppressWarnings("checked") public void testGetTypesUnderEquality4() { // (number,string) and number/string UnionType stringNumber = (UnionType) createUnionType(NUMBER_TYPE, STRING_TYPE); testGetTypeUnderEquality( stringNumber, STRING_

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>Deprecated(true); assertNull(namedGoogBar.getOwnPropertyJSDocInfo("X")); namedGoogBar.setPropertyJSDocInfo("X", info); assertTrue(namedGoogBar.getOwnPropertyJSDocInfo("X").isDeprecated()); assertPropertyTypeInferred(namedGoogBar, "X"); assertTypeEquals(UNKNOWN_TYPE, namedGoogBar.getPropertyType("X")); } public void testGetAndSetJSDocInfoWithObjectTypes() throws Exception { ObjectType sup = registry.createObjectType(registry.createAnonymousObjectType()); ObjectType sub = registry.createObjectType(sup); JSDocInfo deprecated = new JSDocInfo(); deprecated.setDeprecated(true); JSDocInfo privateInfo = new JSDocInfo(); privateInfo.setVisibility(Visibility.PRIVATE); sup.defineProperty("X", NUMBER_TYPE, true, null); sup.setPropertyJSDocInfo("X", privateInfo); sub.defineProperty("X", NUMBER_TYPE, true, null); sub.setPropertyJSDocInfo("X", deprecated); assertFalse(sup.getOwnPropertyJSDocInfo("X").isDeprecated()); assertEquals(Visibility.PRIVATE, sup.getOwnPropertyJSDocInfo("X").getVisibility()); assertTypeEquals(NUMBER_TYPE, sup.getPropertyType("X")); assertTrue(sub.getOwnPropertyJSDocInfo("X").isDeprecated()); assertNull(sub.getOwnPropertyJSDocInfo("X").getVisibility()); assertTypeEquals(NUMBER_TYPE, sub.getPropertyType("X")); } public void testGetAndSetJSDocInfoWithNoType() throws Exception { JSDocInfo deprecated = new JSDocInfo(); deprecated.setDeprecated(true); NO_TYPE.setPropertyJSDocInfo("X", deprecated); assertNull(NO_TYPE.getOwnPropertyJSDocInfo("X")); } public void testObjectGetSubTypes() throws Exception { assertTrue( containsType( OBJECT_FUNCTION_TYPE.getSubTypes(), googBar)); assertTrue( containsType( googBar.getSubTypes(), googSubBar)); assertFalse( containsType( googBar.getSubTypes(), googSubSubBar)); assertFalse( containsType( googSubBar.getSubTypes(), googSubBar)); assertTrue( containsType( googSubBar.getSubTypes

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>Name, int lineno, int charno) { super(registry, registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE)); Preconditions.checkNotNull(reference); this.reference = reference; this.sourceName = sourceName; this.lineno = lineno; this.charno = charno; } @Override boolean defineProperty(String propertyName, JSType type, boolean inferred, Node propertyNode) { if (!isResolved()) { // If this is an unresolved object type, we need to save all its // properties and define them when it is resolved. if (propertyContinuations == null) { propertyContinuations = Lists.newArrayList(); } propertyContinuations.add( new PropertyContinuation( propertyName, type, inferred, propertyNode)); return true; } else { return super.defineProperty( propertyName, type, inferred, propertyNode); } } private void finishPropertyContinuations() { ObjectType referencedObjType = getReferencedObjTypeInternal(); if (referencedObjType != null && !referencedObjType.isUnknownType()) { if (propertyContinuations != null) { for (PropertyContinuation c : propertyContinuations) { c.commit(this); } } } propertyContinuations = null; } /** Returns the type to which this refers (which is unknown if unresolved). */ public JSType getReferencedType() { return getReferencedTypeInternal(); } @Override public String getReferenceName() { return reference; } @Override String toStringHelper(boolean forAnnotations) { return reference; } @Override public boolean hasReferenceName() { return true; } @Override boolean isNamedType() { return true; } @Override public boolean isNominalType() { return true; } /** * Two named types are equivalent if they are the same {@code * ObjectType} object. This is complicated by the fact that isEquivalent * is sometimes called before we have a chance to resolve the type * names. * * @return {@code true} iff {@code that} == {@code this} or {@code that} * is a {@link NamedType} whose reference is the same as ours, * or {@code that} is the type we reference

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> if (value == null) { return null; } // resolving component by component for (int i = 1; i < componentNames.length; i++) { ObjectType parentClass = ObjectType.cast(value); if (parentClass == null) { return null; } if (componentNames[i].length() == 0) { return null; } value = parentClass.getPropertyType(componentNames[i]); } return value; } private void setReferencedAndResolvedType(JSType type, ErrorReporter t, StaticScope<JSType> enclosing) { if (validator != null) { validator.apply(type); } setReferencedType(type); checkEnumElementCycle(t); setResolvedTypeInternal(getReferencedType()); } private void handleTypeCycle(ErrorReporter t) { setReferencedType( registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE)); t.warning("Cycle detected in inheritance chain of type " + reference, sourceName, lineno, charno); setResolvedTypeInternal(getReferencedType()); } private void checkEnumElementCycle(ErrorReporter t) { JSType referencedType = getReferencedType(); if (referencedType instanceof EnumElementType && ((EnumElementType) referencedType).getPrimitiveType() == this) { handleTypeCycle(t); } } // Warns about this type being unresolved iff it's not a forward-declared // type name. private void handleUnresolvedType( ErrorReporter t, boolean ignoreForwardReferencedTypes) { if (registry.isLastGeneration()) { boolean isForwardDeclared = ignoreForwardReferencedTypes && registry.isForwardDeclaredType(reference); if (!isForwardDeclared && registry.isLastGeneration()) { t.warning("Bad type annotation. Unknown type " + reference, sourceName, lineno, charno); } else { setReferencedType( registry.getNativeObjectType( JSTypeNative.NO_RESOLVED_TYPE)); if (registry.isLastGeneration() && validator != null) { validator.apply(getReferencedType()); } } setResolvedTypeInternal(getReferencedType()); } else { setResolvedTypeInternal(this); } } JSType getTypedefType

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> public JSTypeSystem(AbstractCompiler compiler) { registry = compiler.getTypeRegistry(); invalidatingTypes = Sets.newHashSet( registry.getNativeType(JSTypeNative.ALL_TYPE), registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE), registry.getNativeType(JSTypeNative.NO_TYPE), registry.getNativeType(JSTypeNative.FUNCTION_PROTOTYPE), registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE), registry.getNativeType(JSTypeNative.OBJECT_PROTOTYPE), registry.getNativeType(JSTypeNative.TOP_LEVEL_PROTOTYPE), registry.getNativeType(JSTypeNative.UNKNOWN_TYPE)); } @Override public void addInvalidatingType(JSType type) { checkState(!type.isUnionType()); invalidatingTypes.add(type); } @Override public StaticScope<JSType> getRootScope() { return null; } @Override public StaticScope<JSType> getFunctionScope(Node node) { return null; } @Override public JSType getType( StaticScope<JSType> scope, Node node, String prop) { if (node.getJSType() == null) { return registry.getNativeType(JSTypeNative.UNKNOWN_TYPE); } return node.getJSType(); } @Override public boolean isInvalidatingType(JSType type) { if (type == null || invalidatingTypes.contains(type) || type.isUnknownType() /* unresolved types */) { return true; } ObjectType objType = ObjectType.cast(type); return objType != null && !objType.hasReferenceName(); } @Override public ImmutableSet<JSType> getTypesToSkipForType(JSType type) { type = type.restrictByNotNullOrUndefined(); if (type.isUnionType()) { Set<JSType> types = Sets.newHashSet(type); for (JSType alt : type.toMaybeUnionType().getAlternates()) { types.addAll(getTypesToSkipForTypeNonUnion(type)); } return ImmutableSet.copyOf(types); } else if (type.isEnumElementType()) { return getTypesToSkipForType( type

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> return referencedObjType == null ? null : referencedObjType.getImplicitPrototype(); } @Override boolean defineProperty(String propertyName, JSType type, boolean inferred, Node propertyNode) { return referencedObjType == null ? true : referencedObjType.defineProperty( propertyName, type, inferred, propertyNode); } @Override public boolean removeProperty(String name) { return referencedObjType == null ? false : referencedObjType.removeProperty(name); } @Override public boolean isPropertyTypeDeclared(String propertyName) { return referencedObjType == null ? false : referencedObjType.isPropertyTypeDeclared(propertyName); } @Override public Node getPropertyNode(String propertyName) { return referencedObjType == null ? null : referencedObjType.getPropertyNode(propertyName); } @Override public boolean isPropertyTypeInferred(String propertyName) { return referencedObjType == null ? false : referencedObjType.isPropertyTypeInferred(propertyName); } @Override public boolean isPropertyInExterns(String propertyName) { return referencedObjType == null ? false : referencedObjType.isPropertyInExterns(propertyName); } @Override public int getPropertiesCount() { return referencedObjType == null ? 0 : referencedObjType.getPropertiesCount(); } @Override protected void collectPropertyNames(Set<String> props) { if (referencedObjType != null) { referencedObjType.collectPropertyNames(props); } } @Override public JSType findPropertyType(String propertyName) { return referencedType.findPropertyType(propertyName); } @Override public JSType getPropertyType(String propertyName) { return referencedObjType == null ? getNativeType(JSTypeNative.UNKNOWN_TYPE) : referencedObjType.getPropertyType(propertyName); } @Override public JSDocInfo getJSDocInfo() { return referencedType.getJSDocInfo(); } @Override public void setJSDocInfo(JSDocInfo info) { if (referencedObjType != null) { referencedObjType.setJSDocInfo(info); } } @Override public JSDocInfo getOwnPropertyJSDocInfo(String propertyName) { return referencedObjType == null ? null : referencedObjType.getOwnPropertyJSDocInfo(propertyName); } @

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>JSType that) { UnionTypeBuilder thisRestricted = new UnionTypeBuilder(registry); UnionTypeBuilder thatRestricted = new UnionTypeBuilder(registry); for (JSType element : alternates) { TypePair p = element.getTypesUnderShallowInequality(that); if (p.typeA != null) { thisRestricted.addAlternate(p.typeA); } if (p.typeB != null) { thatRestricted.addAlternate(p.typeB); } } return new TypePair( thisRestricted.build(), thatRestricted.build()); } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseUnionType(this); } @Override JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) { setResolvedTypeInternal(this); // for circularly defined types. boolean changed = false; ImmutableList.Builder<JSType> resolvedTypes = ImmutableList.builder(); for (JSType alternate : alternates) { JSType newAlternate = alternate.resolve(t, scope); changed |= (alternate != newAlternate); resolvedTypes.add(alternate); } if (changed) { Collection<JSType> newAlternates = resolvedTypes.build(); Preconditions.checkState( newAlternates.hashCode() == this.hashcode); alternates = newAlternates; } return this; } @Override public String toDebugHashCodeString() { List<String> hashCodes = Lists.newArrayList(); for (JSType a : alternates) { hashCodes.add(a.toDebugHashCodeString()); } return "{(" + Joiner.on(",").join(hashCodes) + ")}"; } @Override public boolean setValidator(Predicate<JSType> validator) { for (JSType a : alternates) { a.setValidator(validator); } return true; } @Override public JSType collapseUnion() { JSType currentValue = null; ObjectType currentCommonSuper = null; for (JSType a : alternates) { if (a.isUnknownType()) { return getNativeType(JSTypeNative.UNKNOWN_TYPE); } ObjectType obj = a.toObjectType

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> BOOLEAN_TYPE, BOOLEAN_OBJECT_TYPE, BOOLEAN_OBJECT_FUNCTION_TYPE, /** * A checked unknown type is a type that we know something about, * but we're not really sure what we know about it. * * Examples of checked unknown types include: * <code> * if (x) { // x is unknown * alert(x); // x is checked unknown * } * </code> * * <code> * /* @param {SomeForwardDeclaredType} x / * function f(x) { * // x is checked unknown. We know it's some type, but the type * // has not been included in this binary. * } * </code> * * This is useful for missing property warnings, where we don't * want to emit warnings on things that have been checked. */ CHECKED_UNKNOWN_TYPE, DATE_TYPE, DATE_FUNCTION_TYPE, ERROR_FUNCTION_TYPE, ERROR_TYPE, EVAL_ERROR_FUNCTION_TYPE, EVAL_ERROR_TYPE, FUNCTION_FUNCTION_TYPE, FUNCTION_INSTANCE_TYPE, // equivalent to U2U_CONSTRUCTOR_TYPE FUNCTION_PROTOTYPE, NULL_TYPE, NUMBER_TYPE, NUMBER_OBJECT_TYPE, NUMBER_OBJECT_FUNCTION_TYPE, OBJECT_TYPE, OBJECT_FUNCTION_TYPE, OBJECT_PROTOTYPE, RANGE_ERROR_FUNCTION_TYPE, RANGE_ERROR_TYPE, REFERENCE_ERROR_FUNCTION_TYPE, REFERENCE_ERROR_TYPE, REGEXP_TYPE, REGEXP_FUNCTION_TYPE, STRING_OBJECT_TYPE, STRING_OBJECT_FUNCTION_TYPE, STRING_TYPE, SYNTAX_ERROR_FUNCTION_TYPE, SYNTAX_ERROR_TYPE, TYPE_ERROR_FUNCTION_TYPE, TYPE_ERROR_TYPE, UNKNOWN_TYPE, URI_ERROR_FUNCTION_TYPE, URI_ERROR_TYPE, VOID_TYPE, // Commonly used types TOP_LEVEL_PROTOTYPE, STRING_VALUE_OR_OBJECT_TYPE, NUMBER_VALUE_OR_OBJECT_TYPE, ALL_TYPE, NO_TYPE, NO_OBJECT_TYPE,

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>iserScopeKnowingConditionOutcome(Node condition, FlowScope blindScope, boolean outcome) { return firstLink.getPreciserScopeKnowingConditionOutcome( condition, blindScope, outcome); } /** * Delegates the calculation of the preciser scope to the next link. * If there is no next link, returns the blind scope. */ protected FlowScope nextPreciserScopeKnowingConditionOutcome(Node condition, FlowScope blindScope, boolean outcome) { return nextLink != null ? nextLink.getPreciserScopeKnowingConditionOutcome( condition, blindScope, outcome) : blindScope; } /** * Returns the type of a node in the given scope if the node corresponds to a * name whose type is capable of being refined. * @return The current type of the node if it can be refined, null otherwise. */ protected JSType getTypeIfRefinable(Node node, FlowScope scope) { switch (node.getType()) { case Token.NAME: StaticSlot<JSType> nameVar = scope.getSlot(node.getString()); if (nameVar != null) { JSType nameVarType = nameVar.getType(); if (nameVarType == null) { nameVarType = node.getJSType(); } return nameVarType; } return null; case Token.GETPROP: String qualifiedName = node.getQualifiedName(); if (qualifiedName == null) { return null; } StaticSlot<JSType> propVar = scope.getSlot(qualifiedName); JSType propVarType = null; if (propVar != null) { propVarType = propVar.getType(); } if (propVarType == null) { propVarType = node.getJSType(); } if (propVarType == null) { propVarType = getNativeType(UNKNOWN_TYPE); } return propVarType; } return null; } /** * Declares a refined type in {@code scope} for the name represented by * {@code node}. It must be possible to refine the type of the given node in * the given scope, as determined by {@link #getTypeIfRefinable}. */ protected void declare

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>NameInScope(FlowScope scope, Node node, JSType type) { switch (node.getType()) { case Token.NAME: scope.inferSlotType(node.getString(), type); break; case Token.GETPROP: String qualifiedName = node.getQualifiedName(); Preconditions.checkNotNull(qualifiedName); JSType origType = node.getJSType(); origType = origType == null ? getNativeType(UNKNOWN_TYPE) : origType; scope.inferQualifiedSlot(node, qualifiedName, origType, type); break; case Token.THIS: // "this" references aren't currently modeled in the CFG. break; default: throw new IllegalArgumentException("Node cannot be refined. \n" + node.toStringTree()); } } /** * @see #getRestrictedWithoutUndefined(JSType) */ private final Visitor<JSType> restrictUndefinedVisitor = new Visitor<JSType>() { @Override public JSType caseEnumElementType(EnumElementType enumElementType) { JSType type = enumElementType.getPrimitiveType().visit(this); if (type != null && enumElementType.getPrimitiveType().equals(type)) { return enumElementType; } else { return type; } } @Override public JSType caseAllType() { return typeRegistry.createUnionType(OBJECT_TYPE, NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE, NULL_TYPE); } @Override public JSType caseNoObjectType() { return getNativeType(NO_OBJECT_TYPE); } @Override public JSType caseNoType() { return getNativeType(NO_TYPE); } @Override public JSType caseBooleanType() { return getNativeType(BOOLEAN_TYPE); } @Override public JSType caseFunctionType(FunctionType type) { return type; } @Override public JSType caseNullType() { return getNativeType(NULL_TYPE); } @Override public JSType caseNumberType() { return getNativeType(NUMBER_TYPE); } @Override public JSType caseObjectType(ObjectType type) { return type; } @Override public JSType caseStringType() { return getNativeType(STRING_TYPE);

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> } @Override public JSType caseUnionType(UnionType type) { return type.getRestrictedUnion(getNativeType(VOID_TYPE)); } @Override public JSType caseUnknownType() { return getNativeType(UNKNOWN_TYPE); } @Override public JSType caseVoidType() { return null; } @Override public JSType caseParameterizedType(ParameterizedType type) { return caseObjectType(type); } @Override public JSType caseTemplateType(TemplateType templateType) { return caseObjectType(templateType); } }; /** * @see #getRestrictedWithoutNull(JSType) */ private final Visitor<JSType> restrictNullVisitor = new Visitor<JSType>() { @Override public JSType caseEnumElementType(EnumElementType enumElementType) { JSType type = enumElementType.getPrimitiveType().visit(this); if (type != null && enumElementType.getPrimitiveType().equals(type)) { return enumElementType; } else { return type; } } @Override public JSType caseAllType() { return typeRegistry.createUnionType(OBJECT_TYPE, NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE, VOID_TYPE); } @Override public JSType caseNoObjectType() { return getNativeType(NO_OBJECT_TYPE); } @Override public JSType caseNoType() { return getNativeType(NO_TYPE); } @Override public JSType caseBooleanType() { return getNativeType(BOOLEAN_TYPE); } @Override public JSType caseFunctionType(FunctionType type) { return type; } @Override public JSType caseNullType() { return null; } @Override public JSType caseNumberType() { return getNativeType(NUMBER_TYPE); } @Override public JSType caseObjectType(ObjectType type) { return type; } @Override public JSType caseStringType() { return getNativeType(STRING_TYPE); } @Override public JSType caseUnionType(UnionType type) { return type.getRestrictedUnion(getNativeType(NULL_TYPE)); } @Override public JSType caseUnknownType() { return

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> getNativeType(UNKNOWN_TYPE); } @Override public JSType caseVoidType() { return getNativeType(VOID_TYPE); } @Override public JSType caseParameterizedType(ParameterizedType type) { return caseObjectType(type); } @Override public JSType caseTemplateType(TemplateType templateType) { return caseObjectType(templateType); } }; /** * A class common to all visitors that need to restrict the type based on * {@code typeof}-like conditions. */ abstract class RestrictByTypeOfResultVisitor implements Visitor<JSType> { /** * Abstracts away the similarities between visiting the unknown type and the * all type. * @param topType {@code UNKNOWN_TYPE} or {@code ALL_TYPE} * @return the restricted type * @see #caseAllType * @see #caseUnknownType */ protected abstract JSType caseTopType(JSType topType); @Override public JSType caseAllType() { return caseTopType(getNativeType(ALL_TYPE)); } @Override public JSType caseUnknownType() { return caseTopType(getNativeType(UNKNOWN_TYPE)); } @Override public JSType caseUnionType(UnionType type) { JSType restricted = null; for (JSType alternate : type.getAlternates()) { JSType restrictedAlternate = alternate.visit(this); if (restrictedAlternate != null) { if (restricted == null) { restricted = restrictedAlternate; } else { restricted = restrictedAlternate.getLeastSupertype(restricted); } } } return restricted; } @Override public JSType caseNoType() { return getNativeType(NO_TYPE); } @Override public JSType caseEnumElementType(EnumElementType enumElementType) { // NOTE(nicksantos): This is a white lie. Suppose we have: // /** @enum {string|number} */ var MyEnum = ...; // if (goog.isNumber(myEnumInstance)) { // /* what is myEnumInstance here? */ // } // There is no type that represents {MyEnum - string}. What we really // need is a notion of "enum subtyping",

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>number"</td></tr> * <tr><td>{@code string}</td><td>"string"</td></tr> * <tr><td>{@code Object} (which doesn't implement [[Call]])</td> * <td>"object"</td></tr> * <tr><td>{@code Object} (which implements [[Call]])</td> * <td>"function"</td></tr> * </table> * @param type the type to restrict * @param value A value known to be equal or not equal to the result of the * {@code typeof} operation * @param resultEqualsValue {@code true} if the {@code typeOf} result is known * to equal {@code value}; {@code false} if it is known <em>not</em> to * equal {@code value} * @return the restricted type or null if no version of the type matches the * restriction */ JSType getRestrictedByTypeOfResult(JSType type, String value, boolean resultEqualsValue) { if (type == null) { if (resultEqualsValue) { JSType result = getNativeTypeForTypeOf(value); return result == null ? getNativeType(UNKNOWN_TYPE) : result; } else { return null; } } return type.visit( new RestrictByOneTypeOfResultVisitor(value, resultEqualsValue)); } JSType getNativeType(JSTypeNative typeId) { return typeRegistry.getNativeType(typeId); } /** * If we definitely know what a type is based on the typeof result, * return it. Otherwise, return null. * * The typeof operation in JS is poorly defined, and this function works * for both the native typeof and goog.typeOf. It should not be made public, * because its semantics are informally defined, and would be wrong in * the general case. */ private JSType getNativeTypeForTypeOf(String value) { if (value.equals("number")) { return getNativeType(NUMBER_TYPE); } else if (value.equals("boolean")) { return getNativeType(BOOLEAN_TYPE); } else if (value.equals("string")) { return getNativeType(STRING_TYPE);

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> are duplicates of // one another. int currentIndex = 0; Iterator<JSType> it = alternates.iterator(); while (it.hasNext()) { JSType current = it.next(); // Unknown and NoResolved types may just be names that haven't // been resolved yet. So keep these in the union, and just use // equality checking for simple de-duping. if (alternate.isUnknownType() || current.isUnknownType() || alternate.isNoResolvedType() || current.isNoResolvedType() || alternate.hasAnyTemplate() || current.hasAnyTemplate()) { if (alternate.isEquivalentTo(current)) { // Alternate is unnecessary. return this; } } else { if (alternate.isSubtype(current)) { // Alternate is unnecessary. return this; } else if (current.isSubtype(alternate)) { // Alternate makes current obsolete it.remove(); if (currentIndex == functionTypePosition) { functionTypePosition = -1; } else if (currentIndex < functionTypePosition) { functionTypePosition--; currentIndex--; } } } currentIndex++; } if (alternate.isFunctionType()) { // See the comments on functionTypePosition above. Preconditions.checkState(functionTypePosition == -1); functionTypePosition = alternates.size(); } alternates.add(alternate); result = null; // invalidate the memoized result } } else { result = null; } return this; } /** * Reduce the alternates into a non-union type. * If the alternates can't be accurately represented with a non-union * type, return null. */ private JSType reduceAlternatesWithoutUnion() { if (isAllType) { return registry.getNativeType(ALL_TYPE); } else if (isNativeUnknownType) { if (areAllUnknownsChecked) { return registry.getNativeType(CHECKED_UNKNOWN_TYPE); } else { return registry.getNativeType(UNKNOWN_TYPE); } } else { int size = alternates.size(); if (size > maxUnionSize) { return registry.getNativeType(UNKNOWN_TYPE); } else

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>()); } } return false; } /** * If this is equal to a NamedType object, its hashCode must be equal * to the hashCode of the NamedType object. */ @Override public int hashCode() { if (hasReferenceName()) { return getReferenceName().hashCode(); } else { return super.hashCode(); } } @Override String toStringHelper(boolean forAnnotations) { return forAnnotations ? primitiveType.toString() : (getReferenceName() + ".<" + primitiveType + ">"); } @Override public String getReferenceName() { return name; } @Override public boolean hasReferenceName() { return true; } @Override public boolean isSubtype(JSType that) { if (JSType.isSubtypeHelper(this, that)) { return true; } else { return primitiveType.isSubtype(that); } } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseEnumElementType(this); } @Override boolean defineProperty(String propertyName, JSType type, boolean inferred, Node propertyNode) { // nothing return true; } @Override public boolean isPropertyTypeDeclared(String propertyName) { return primitiveObjectType == null ? false : primitiveObjectType.isPropertyTypeDeclared(propertyName); } @Override public boolean isPropertyTypeInferred(String propertyName) { return primitiveObjectType == null ? false : primitiveObjectType.isPropertyTypeInferred(propertyName); } @Override public ObjectType getImplicitPrototype() { return null; } @Override public int getPropertiesCount() { return primitiveObjectType == null ? 0 : primitiveObjectType.getPropertiesCount(); } @Override void collectPropertyNames(Set<String> props) { if (primitiveObjectType != null) { primitiveObjectType.collectPropertyNames(props); } } @Override public JSType findPropertyType(String propertyName) { return primitiveType.findPropertyType(propertyName); } @Override public JSType getPropertyType(String propertyName) { return primitiveObjectType == null ? getNativeType(JSTypeNative.UNKNOWN_TYPE) : primitiveObjectType.getPropertyType(propertyName); } @Override public boolean hasProperty

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> // The subclass method might write its var_args as individual // arguments. if (currentParam.getNext() != null && newParam.isVarArgs()) { newParam.setVarArgs(false); newParam.setOptionalArg(true); } } else { warnedAboutArgList |= addParameter( paramBuilder, typeRegistry.getNativeType(UNKNOWN_TYPE), warnedAboutArgList, codingConvention.isOptionalParameter(currentParam) || oldParamsListHitOptArgs, codingConvention.isVarArgsParameter(currentParam)); } } // Clone any remaining params that aren't in the function literal, // but make them optional. while (oldParams.hasNext()) { paramBuilder.newOptionalParameterFromNode(oldParams.next()); } parametersNode = paramBuilder.build(); } return this; } /** * Infer the return type from JSDocInfo. */ FunctionTypeBuilder inferReturnType(@Nullable JSDocInfo info) { if (info != null && info.hasReturnType()) { returnType = info.getReturnType().evaluate(scope, typeRegistry); returnTypeInferred = false; } return this; } /** * Infer the role of the function (whether it's a constructor or interface) * and what it inherits from in JSDocInfo. */ FunctionTypeBuilder inferInheritance(@Nullable JSDocInfo info) { if (info != null) { isConstructor = info.isConstructor(); isInterface = info.isInterface(); // base type if (info.hasBaseType()) { if (isConstructor) { JSType maybeBaseType = info.getBaseType().evaluate(scope, typeRegistry); if (maybeBaseType != null && maybeBaseType.setValidator(new ExtendedTypeValidator())) { baseType = (ObjectType) maybeBaseType; } } else { reportWarning(EXTENDS_WITHOUT_TYPEDEF, fnName); } } // implemented interfaces if (isConstructor || isInterface) { implementedInterfaces = Lists.newArrayList(); for (JSTypeExpression t : info.getImplementedInterfaces()) { JSType maybeInterType = t.evaluate(scope, typeRegistry); if (maybeInterType != null && maybeInterType.

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> the parameter types from the list of argument names and * the doc info. */ FunctionTypeBuilder inferParameterTypes(@Nullable Node argsParent, @Nullable JSDocInfo info) { if (argsParent == null) { if (info == null) { return this; } else { return inferParameterTypes(info); } } // arguments Node oldParameterType = null; if (parametersNode != null) { oldParameterType = parametersNode.getFirstChild(); } FunctionParamBuilder builder = new FunctionParamBuilder(typeRegistry); boolean warnedAboutArgList = false; Set<String> allJsDocParams = (info == null) ? Sets.<String>newHashSet() : Sets.newHashSet(info.getParameterNames()); boolean foundTemplateType = false; boolean isVarArgs = false; for (Node arg : argsParent.children()) { String argumentName = arg.getString(); allJsDocParams.remove(argumentName); // type from JSDocInfo JSType parameterType = null; boolean isOptionalParam = isOptionalParameter(arg, info); isVarArgs = isVarArgsParameter(arg, info); if (info != null && info.hasParameterType(argumentName)) { parameterType = info.getParameterType(argumentName).evaluate(scope, typeRegistry); } else if (oldParameterType != null && oldParameterType.getJSType() != null) { parameterType = oldParameterType.getJSType(); isOptionalParam = oldParameterType.isOptionalArg(); isVarArgs = oldParameterType.isVarArgs(); } else { parameterType = typeRegistry.getNativeType(UNKNOWN_TYPE); } warnedAboutArgList |= addParameter( builder, parameterType, warnedAboutArgList, isOptionalParam, isVarArgs); if (oldParameterType != null) { oldParameterType = oldParameterType.getNext(); } } // Copy over any old parameters that aren't in the param list. if (!isVarArgs) { while (oldParameterType != null && !isVarArgs) { builder.newParameterFromNode(oldParameterType); oldParameterType = oldParameterType.getNext(); } } for (String inexistentName : allJsDocParams) { reportWarning(IN

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>Warning(VAR_ARGS_MUST_BE_LAST); emittedWarning = true; } } else if (isVarArgs) { if (!builder.addVarArgs(paramType) && !warnedAboutArgList) { reportWarning(VAR_ARGS_MUST_BE_LAST); emittedWarning = true; } } else { if (!builder.addRequiredParams(paramType) && !warnedAboutArgList) { // An optional parameter was seen and this argument is not an optional // or var arg so it is an error. if (builder.hasVarArgs()) { reportWarning(VAR_ARGS_MUST_BE_LAST); } else { reportWarning(OPTIONAL_ARG_AT_END); } emittedWarning = true; } } return emittedWarning; } /** * Builds the function type, and puts it in the registry. */ FunctionType buildAndRegister() { if (returnType == null) { // Infer return types. // We need to be extremely conservative about this, because of two // competing needs. // 1) If we infer the return type of f too widely, then we won't be able // to assign f to other functions. // 2) If we infer the return type of f too narrowly, then we won't be // able to override f in subclasses. // So we only infer in cases where the user doesn't expect to write // @return annotations--when it's very obvious that the function returns // nothing. if (!contents.mayHaveNonEmptyReturns() && !contents.mayHaveSingleThrow() && !contents.mayBeFromExterns()) { returnType = typeRegistry.getNativeType(VOID_TYPE); returnTypeInferred = true; } } if (returnType == null) { returnType = typeRegistry.getNativeType(UNKNOWN_TYPE); } if (parametersNode == null) { throw new IllegalStateException( "All Function types must have params and a return type"); } FunctionType fnType; if (isConstructor) { fnType = getOrCreateConstructor(); } else if (isInterface) { fnType = typeRegistry.createInterfaceType( fnName, contents.getSourceNode()); if (

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> ObjectType classA = googAFunction.getInstanceType(); assertEquals(NATIVE_PROPERTIES_COUNT + 1, classA.getPropertiesCount()); checkObjectType(classA, "m1", NUMBER_TYPE); } public void testAddingMethodsPrototypeIdiomAndObjectLiteralSimpleNamespace() throws Exception { Node js1Node = parseAndTypeCheck( "/** @constructor */function A() {}" + "A.prototype = {m1: 5, m2: true}"); ObjectType instanceType = getInstanceType(js1Node); assertEquals(NATIVE_PROPERTIES_COUNT + 2, instanceType.getPropertiesCount()); checkObjectType(instanceType, "m1", NUMBER_TYPE); checkObjectType(instanceType, "m2", BOOLEAN_TYPE); } public void testDontAddMethodsIfNoConstructor() throws Exception { Node js1Node = parseAndTypeCheck( "function A() {}" + "A.prototype = {m1: 5, m2: true}"); JSType functionAType = js1Node.getFirstChild().getJSType(); assertEquals("function (): undefined", functionAType.toString()); assertEquals(UNKNOWN_TYPE, U2U_FUNCTION_TYPE.getPropertyType("m1")); assertEquals(UNKNOWN_TYPE, U2U_FUNCTION_TYPE.getPropertyType("m2")); } public void testFunctionAssignement() throws Exception { testTypes("/**" + "* @param {string} ph0" + "* @param {string} ph1" + "* @return {string}" + "*/" + "function MSG_CALENDAR_ACCESS_ERROR(ph0, ph1) {return ''}" + "/** @type {Function} */" + "var MSG_CALENDAR_ADD_ERROR = MSG_CALENDAR_ACCESS_ERROR;"); } public void testAddMethodsPrototypeTwoWays() throws Exception { Node js1Node = parseAndTypeCheck( "/** @constructor */function A() {}" + "A.prototype = {m1: 5, m2: true};" + "A.prototype.m3 = 'third property!';"); ObjectType instanceType = getInstanceType(js1Node); assertEquals("A", instanceType.toString()); assertEquals(NATIVE_

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>) { this.resolveMode = mode; } ResolveMode getResolveMode() { return resolveMode; } public ErrorReporter getErrorReporter() { return reporter; } public boolean shouldTolerateUndefinedValues() { return tolerateUndefinedValues; } /** * Reset to run the TypeCheck pass. */ public void resetForTypeCheck() { typesIndexedByProperty.clear(); eachRefTypeIndexedByProperty.clear(); initializeBuiltInTypes(); namesToTypes.clear(); namespaces.clear(); initializeRegistry(); } private void initializeBuiltInTypes() { // These locals shouldn't be all caps. BooleanType BOOLEAN_TYPE = new BooleanType(this); registerNativeType(JSTypeNative.BOOLEAN_TYPE, BOOLEAN_TYPE); NullType NULL_TYPE = new NullType(this); registerNativeType(JSTypeNative.NULL_TYPE, NULL_TYPE); NumberType NUMBER_TYPE = new NumberType(this); registerNativeType(JSTypeNative.NUMBER_TYPE, NUMBER_TYPE); StringType STRING_TYPE = new StringType(this); registerNativeType(JSTypeNative.STRING_TYPE, STRING_TYPE); UnknownType UNKNOWN_TYPE = new UnknownType(this, false); registerNativeType(JSTypeNative.UNKNOWN_TYPE, UNKNOWN_TYPE); registerNativeType( JSTypeNative.CHECKED_UNKNOWN_TYPE, new UnknownType(this, true)); VoidType VOID_TYPE = new VoidType(this); registerNativeType(JSTypeNative.VOID_TYPE, VOID_TYPE); AllType ALL_TYPE = new AllType(this); registerNativeType(JSTypeNative.ALL_TYPE, ALL_TYPE); // Top Level Prototype (the One) // The initializations of TOP_LEVEL_PROTOTYPE and OBJECT_FUNCTION_TYPE // use each other's results, so at least one of them will get null // instead of an actual type; however, this seems to be benign. PrototypeObjectType TOP_LEVEL_PROTOTYPE = new PrototypeObjectType(this, null, null, true); registerNativeType(JSTypeNative.TOP_LEVEL_PROTOTYPE, TOP_LEVEL_PROTOTYPE); // Object FunctionType OBJECT_FUNCTION_TYPE = new Function

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>Type(this, "Object", null, createArrowType(createOptionalParameters(ALL_TYPE), UNKNOWN_TYPE), null, null, true, true); OBJECT_FUNCTION_TYPE.setPrototype(TOP_LEVEL_PROTOTYPE, null); registerNativeType(JSTypeNative.OBJECT_FUNCTION_TYPE, OBJECT_FUNCTION_TYPE); ObjectType OBJECT_TYPE = OBJECT_FUNCTION_TYPE.getInstanceType(); registerNativeType(JSTypeNative.OBJECT_TYPE, OBJECT_TYPE); ObjectType OBJECT_PROTOTYPE = OBJECT_FUNCTION_TYPE.getPrototype(); registerNativeType(JSTypeNative.OBJECT_PROTOTYPE, OBJECT_PROTOTYPE); // Function FunctionType FUNCTION_FUNCTION_TYPE = new FunctionType(this, "Function", null, createArrowType( createParametersWithVarArgs(ALL_TYPE), UNKNOWN_TYPE), null, null, true, true); FUNCTION_FUNCTION_TYPE.setPrototypeBasedOn(OBJECT_TYPE); registerNativeType( JSTypeNative.FUNCTION_FUNCTION_TYPE, FUNCTION_FUNCTION_TYPE); ObjectType FUNCTION_PROTOTYPE = FUNCTION_FUNCTION_TYPE.getPrototype(); registerNativeType(JSTypeNative.FUNCTION_PROTOTYPE, FUNCTION_PROTOTYPE); NoType NO_TYPE = new NoType(this); registerNativeType(JSTypeNative.NO_TYPE, NO_TYPE); NoObjectType NO_OBJECT_TYPE = new NoObjectType(this); registerNativeType(JSTypeNative.NO_OBJECT_TYPE, NO_OBJECT_TYPE); NoObjectType NO_RESOLVED_TYPE = new NoResolvedType(this); registerNativeType(JSTypeNative.NO_RESOLVED_TYPE, NO_RESOLVED_TYPE); // Array FunctionType ARRAY_FUNCTION_TYPE = new FunctionType(this, "Array", null, createArrowType(createParametersWithVarArgs(ALL_TYPE), null), null, null, true, true); ARRAY_FUNCTION_TYPE.getInternalArrowType().returnType = ARRAY_FUNCTION_TYPE.getInstanceType(); ObjectType arrayPrototype = ARRAY_FUNCTION_TYPE.getPrototype(); registerNativeType(JSTypeNative.ARRAY_FUNCTION_TYPE, ARRAY_FUNCTION_TYPE); ObjectType ARRAY_TYPE = ARRAY_FUNCTION_TYPE.getInstanceType(); registerNativeType

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>(JSTypeNative.ARRAY_TYPE, ARRAY_TYPE); // Boolean FunctionType BOOLEAN_OBJECT_FUNCTION_TYPE = new FunctionType(this, "Boolean", null, createArrowType(createParameters(false, ALL_TYPE), BOOLEAN_TYPE), null, null, true, true); ObjectType booleanPrototype = BOOLEAN_OBJECT_FUNCTION_TYPE.getPrototype(); registerNativeType( JSTypeNative.BOOLEAN_OBJECT_FUNCTION_TYPE, BOOLEAN_OBJECT_FUNCTION_TYPE); ObjectType BOOLEAN_OBJECT_TYPE = BOOLEAN_OBJECT_FUNCTION_TYPE.getInstanceType(); registerNativeType(JSTypeNative.BOOLEAN_OBJECT_TYPE, BOOLEAN_OBJECT_TYPE); // Date FunctionType DATE_FUNCTION_TYPE = new FunctionType(this, "Date", null, createArrowType( createOptionalParameters(UNKNOWN_TYPE, UNKNOWN_TYPE, UNKNOWN_TYPE, UNKNOWN_TYPE, UNKNOWN_TYPE, UNKNOWN_TYPE, UNKNOWN_TYPE), STRING_TYPE), null, null, true, true); ObjectType datePrototype = DATE_FUNCTION_TYPE.getPrototype(); registerNativeType(JSTypeNative.DATE_FUNCTION_TYPE, DATE_FUNCTION_TYPE); ObjectType DATE_TYPE = DATE_FUNCTION_TYPE.getInstanceType(); registerNativeType(JSTypeNative.DATE_TYPE, DATE_TYPE); // Error FunctionType ERROR_FUNCTION_TYPE = new ErrorFunctionType(this, "Error"); registerNativeType(JSTypeNative.ERROR_FUNCTION_TYPE, ERROR_FUNCTION_TYPE); ObjectType ERROR_TYPE = ERROR_FUNCTION_TYPE.getInstanceType(); registerNativeType(JSTypeNative.ERROR_TYPE, ERROR_TYPE); // EvalError FunctionType EVAL_ERROR_FUNCTION_TYPE = new ErrorFunctionType(this, "EvalError"); EVAL_ERROR_FUNCTION_TYPE.setPrototypeBasedOn(ERROR_TYPE); registerNativeType( JSTypeNative.EVAL_ERROR_FUNCTION_TYPE, EVAL_ERROR_FUNCTION_TYPE); ObjectType EVAL_ERROR_TYPE = EVAL_ERROR_FUNCTION_TYPE.getInstanceType(); registerNativeType(JSTypeNative.EVAL_ERROR_TYPE, EVAL_ERROR_TYPE); // RangeError FunctionType RANGE_ERROR_FUNCTION_TYPE = new ErrorFunctionType(this, "Range

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>,number) JSType OBJECT_NUMBER_STRING = createUnionType(OBJECT_TYPE, NUMBER_TYPE, STRING_TYPE); registerNativeType(JSTypeNative.OBJECT_NUMBER_STRING, OBJECT_NUMBER_STRING); // (Object,string,number,boolean) JSType OBJECT_NUMBER_STRING_BOOLEAN = createUnionType(OBJECT_TYPE, NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE); registerNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN, OBJECT_NUMBER_STRING_BOOLEAN); // (string,number,boolean) JSType NUMBER_STRING_BOOLEAN = createUnionType(NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE); registerNativeType(JSTypeNative.NUMBER_STRING_BOOLEAN, NUMBER_STRING_BOOLEAN); // (string,number) JSType NUMBER_STRING = createUnionType(NUMBER_TYPE, STRING_TYPE); registerNativeType(JSTypeNative.NUMBER_STRING, NUMBER_STRING); // Native object properties are filled in by externs... // (String, string) JSType STRING_VALUE_OR_OBJECT_TYPE = createUnionType(STRING_OBJECT_TYPE, STRING_TYPE); registerNativeType( JSTypeNative.STRING_VALUE_OR_OBJECT_TYPE, STRING_VALUE_OR_OBJECT_TYPE); // (Number, number) JSType NUMBER_VALUE_OR_OBJECT_TYPE = createUnionType(NUMBER_OBJECT_TYPE, NUMBER_TYPE); registerNativeType( JSTypeNative.NUMBER_VALUE_OR_OBJECT_TYPE, NUMBER_VALUE_OR_OBJECT_TYPE); // unknown function type, i.e. (?...) -> ? FunctionType U2U_FUNCTION_TYPE = createFunctionType(UNKNOWN_TYPE, true, UNKNOWN_TYPE); registerNativeType(JSTypeNative.U2U_FUNCTION_TYPE, U2U_FUNCTION_TYPE); // unknown constructor type, i.e. (?...) -> ? with the NoObject type // as instance type FunctionType U2U_CONSTRUCTOR_TYPE = // This is equivalent to // createConstructorType(UNKNOWN_TYPE, true, UNKNOWN_TYPE), but, // in addition, overrides getInstanceType() to return the NoObject type // instead of a new anonymous object.

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> new FunctionType(this, "Function", null, createArrowType( createParametersWithVarArgs(UNKNOWN_TYPE), UNKNOWN_TYPE), NO_OBJECT_TYPE, null, true, true) { private static final long serialVersionUID = 1L; @Override public FunctionType getConstructor() { return registry.getNativeFunctionType( JSTypeNative.FUNCTION_FUNCTION_TYPE); } }; // The U2U_CONSTRUCTOR is weird, because it's the supertype of its // own constructor. registerNativeType(JSTypeNative.U2U_CONSTRUCTOR_TYPE, U2U_CONSTRUCTOR_TYPE); registerNativeType( JSTypeNative.FUNCTION_INSTANCE_TYPE, U2U_CONSTRUCTOR_TYPE); FUNCTION_FUNCTION_TYPE.setInstanceType(U2U_CONSTRUCTOR_TYPE); U2U_CONSTRUCTOR_TYPE.setImplicitPrototype(FUNCTION_PROTOTYPE); // least function type, i.e. (All...) -> NoType FunctionType LEAST_FUNCTION_TYPE = createNativeFunctionTypeWithVarArgs(NO_TYPE, ALL_TYPE); registerNativeType(JSTypeNative.LEAST_FUNCTION_TYPE, LEAST_FUNCTION_TYPE); // the 'this' object in the global scope FunctionType GLOBAL_THIS_CTOR = new FunctionType(this, "global this", null, createArrowType(createParameters(false, ALL_TYPE), NUMBER_TYPE), null, null, true, true); ObjectType GLOBAL_THIS = GLOBAL_THIS_CTOR.getInstanceType(); registerNativeType(JSTypeNative.GLOBAL_THIS, GLOBAL_THIS); // greatest function type, i.e. (NoType...) -> All FunctionType GREATEST_FUNCTION_TYPE = createNativeFunctionTypeWithVarArgs(ALL_TYPE, NO_TYPE); registerNativeType(JSTypeNative.GREATEST_FUNCTION_TYPE, GREATEST_FUNCTION_TYPE); // Register the prototype property. See the comments below in // registerPropertyOnType about the bootstrapping process. registerPropertyOnType("prototype", OBJECT_FUNCTION_TYPE); } private void initializeRegistry() { register(getNativeType(JSTypeNative.ARRAY_TYPE)); register(getNativeType(JSTypeNative

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> if (resolveMode == ResolveMode.LAZY_EXPRESSIONS) { // If the type expression doesn't contain any names, just // resolve it anyway. boolean hasNames = hasTypeName(n); if (hasNames) { return new UnresolvedTypeExpression(this, n, sourceName); } } return createFromTypeNodesInternal(n, sourceName, scope); } private boolean hasTypeName(Node n) { if (n.getType() == Token.STRING) { return true; } for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (hasTypeName(child)) { return true; } } return false; } /** @see #createFromTypeNodes(Node, String, StaticScope) */ private JSType createFromTypeNodesInternal(Node n, String sourceName, StaticScope<JSType> scope) { switch (n.getType()) { case Token.LC: // Record type. return createRecordTypeFromNodes( n.getFirstChild(), sourceName, scope); case Token.BANG: // Not nullable return createFromTypeNodesInternal( n.getFirstChild(), sourceName, scope) .restrictByNotNullOrUndefined(); case Token.QMARK: // Nullable or unknown Node firstChild = n.getFirstChild(); if (firstChild == null) { return getNativeType(UNKNOWN_TYPE); } return createDefaultObjectUnion( createFromTypeNodesInternal( firstChild, sourceName, scope)); case Token.EQUALS: // Optional return createOptionalType( createFromTypeNodesInternal( n.getFirstChild(), sourceName, scope)); case Token.ELLIPSIS: // Var args return createOptionalType( createFromTypeNodesInternal( n.getFirstChild(), sourceName, scope)); case Token.STAR: // The AllType return getNativeType(ALL_TYPE); case Token.LB: // Array type // TODO(nicksantos): Enforce membership restrictions on the Array. return getNativeType(ARRAY_TYPE); case Token.PIPE: // Union type UnionTypeBuilder builder = new UnionTypeBuilder(this); for (Node child = n.getFirstChild(); child != null; child =

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> child.getNext()) { builder.addAlternate( createFromTypeNodesInternal(child, sourceName, scope)); } return builder.build(); case Token.EMPTY: // When the return value of a function is not specified return getNativeType(UNKNOWN_TYPE); case Token.VOID: // Only allowed in the return value of a function. return getNativeType(VOID_TYPE); case Token.STRING: JSType namedType = getType(scope, n.getString(), sourceName, n.getLineno(), n.getCharno()); if (resolveMode != ResolveMode.LAZY_NAMES) { namedType = namedType.resolveInternal(reporter, scope); } if ((namedType instanceof ObjectType) && !(nonNullableTypeNames.contains(n.getString()))) { Node typeList = n.getFirstChild(); if (typeList != null && ("Array".equals(n.getString()) || "Object".equals(n.getString()))) { JSType parameterType = createFromTypeNodesInternal( typeList.getLastChild(), sourceName, scope); namedType = new ParameterizedType( this, (ObjectType) namedType, parameterType); if (typeList.hasMoreThanOneChild()) { JSType indexType = createFromTypeNodesInternal( typeList.getFirstChild(), sourceName, scope); namedType = new IndexedType( this, (ObjectType) namedType, indexType); } } return createDefaultObjectUnion(namedType); } else { return namedType; } case Token.FUNCTION: ObjectType thisType = null; boolean isConstructor = false; Node current = n.getFirstChild(); if (current.getType() == Token.THIS || current.getType() == Token.NEW) { Node contextNode = current.getFirstChild(); thisType = ObjectType.cast( createFromTypeNodesInternal( contextNode, sourceName, scope) .restrictByNotNullOrUndefined()); if (thisType == null) { reporter.warning( ScriptRuntime.getMessage0( current.getType() == Token.THIS ? "msg.jsdoc.function.thisnotobject" : "msg.jsdoc.function.newnotobject"), sourceName, contextNode.getLineno

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>(), contextNode.getCharno()); } isConstructor = current.getType() == Token.NEW; current = current.getNext(); } FunctionParamBuilder paramBuilder = new FunctionParamBuilder(this); if (current.getType() == Token.PARAM_LIST) { Node args = current.getFirstChild(); for (Node arg = current.getFirstChild(); arg != null; arg = arg.getNext()) { if (arg.getType() == Token.ELLIPSIS) { if (arg.getChildCount() == 0) { paramBuilder.addVarArgs(getNativeType(UNKNOWN_TYPE)); } else { paramBuilder.addVarArgs( createFromTypeNodesInternal( arg.getFirstChild(), sourceName, scope)); } } else { JSType type = createFromTypeNodesInternal( arg, sourceName, scope); if (arg.getType() == Token.EQUALS) { boolean addSuccess = paramBuilder.addOptionalParams(type); if (!addSuccess) { reporter.warning( ScriptRuntime.getMessage0("msg.jsdoc.function.varargs"), sourceName, arg.getLineno(), arg.getCharno()); } } else { paramBuilder.addRequiredParams(type); } } } current = current.getNext(); } JSType returnType = createFromTypeNodesInternal(current, sourceName, scope); return new FunctionBuilder(this) .withParams(paramBuilder) .withReturnType(returnType) .withTypeOfThis(thisType) .setIsConstructor(isConstructor) .build(); } throw new IllegalStateException( "Unexpected node in type expression: " + n.toString()); } /** * Creates a RecordType from the nodes representing said record type. * @param n The node with type info. * @param sourceName The source file name. * @param scope A scope for doing type name lookups. */ private JSType createRecordTypeFromNodes(Node n, String sourceName, StaticScope<JSType> scope) { RecordTypeBuilder builder = new RecordTypeBuilder(this); // For each of the fields in the record type. for (Node fieldTypeNode = n.getFirstChild(); fieldTypeNode != null; fieldType

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>Node = fieldTypeNode.getNext()) { // Get the property's name. Node fieldNameNode = fieldTypeNode; boolean hasType = false; if (fieldTypeNode.getType() == Token.COLON) { fieldNameNode = fieldTypeNode.getFirstChild(); hasType = true; } String fieldName = fieldNameNode.getString(); // TODO(user): Move this into the lexer/parser. // Remove the string literal characters around a field name, // if any. if (fieldName.startsWith("'") || fieldName.startsWith("\"")) { fieldName = fieldName.substring(1, fieldName.length() - 1); } // Get the property's type. JSType fieldType = null; if (hasType) { // We have a declared type. fieldType = createFromTypeNodesInternal( fieldTypeNode.getLastChild(), sourceName, scope); } else { // Otherwise, the type is UNKNOWN. fieldType = getNativeType(JSTypeNative.UNKNOWN_TYPE); } // Add the property to the record. if (builder.addProperty(fieldName, fieldType, fieldNameNode) == null) { // Duplicate field name, warning and skip reporter.warning( "Duplicate record field " + fieldName, sourceName, n.getLineno(), fieldNameNode.getCharno()); } } return builder.build(); } /** * Sets the template type name. */ public void setTemplateTypeNames(List<String> names) { Preconditions.checkNotNull(names); for (String name : names) { templateTypes.put(name, new TemplateType(this, name)); } } /** * Clears the template type name. */ public void clearTemplateTypeNames() { templateTypes.clear(); } }

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> arrow type from the complex * {@link FunctionType} that models JavaScript's notion of functions. */ final class ArrowType extends JSType { private static final long serialVersionUID = 1L; final Node parameters; JSType returnType; // Whether the return type is inferred. final boolean returnTypeInferred; ArrowType(JSTypeRegistry registry, Node parameters, JSType returnType) { this(registry, parameters, returnType, false); } ArrowType(JSTypeRegistry registry, Node parameters, JSType returnType, boolean returnTypeInferred) { super(registry); this.parameters = parameters == null ? registry.createParametersWithVarArgs(getNativeType(UNKNOWN_TYPE)) : parameters; this.returnType = returnType == null ? getNativeType(UNKNOWN_TYPE) : returnType; this.returnTypeInferred = returnTypeInferred; } @Override public boolean isSubtype(JSType other) { if (!(other instanceof ArrowType)) { return false; } ArrowType that = (ArrowType) other; // This is described in Draft 2 of the ES4 spec, // Section 3.4.7: Subtyping Function Types. // this.returnType <: that.returnType (covariant) if (!this.returnType.isSubtype(that.returnType)) { return false; } // that.paramType[i] <: this.paramType[i] (contravariant) // // If this.paramType[i] is required, // then that.paramType[i] is required. // // In theory, the "required-ness" should work in the other direction as // well. In other words, if we have // // function f(number, number) {} // function g(number) {} // // Then f *should* not be a subtype of g, and g *should* not be // a subtype of f. But in practice, we do not implement it this way. // We want to support the use case where you can pass g where f is // expected, and pretend that g ignores the second argument. // That way, you can have a single "no-op" function, and you don't have // to create a new no-op function for every possible type signature.

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>JSTypeNative; import com.google.javascript.rhino.jstype.JSTypeRegistry; import com.google.javascript.rhino.jstype.ObjectType; import com.google.javascript.rhino.jstype.RecordTypeBuilder; import junit.framework.TestCase; public abstract class BaseJSTypeTestCase extends TestCase { protected JSTypeRegistry registry; protected TestErrorReporter errorReporter; protected JSType ALL_TYPE; protected ObjectType NO_OBJECT_TYPE; protected ObjectType NO_TYPE; protected ObjectType NO_RESOLVED_TYPE; protected JSType ARRAY_FUNCTION_TYPE; protected ObjectType ARRAY_TYPE; protected JSType BOOLEAN_OBJECT_FUNCTION_TYPE; protected ObjectType BOOLEAN_OBJECT_TYPE; protected JSType BOOLEAN_TYPE; protected JSType CHECKED_UNKNOWN_TYPE; protected JSType DATE_FUNCTION_TYPE; protected ObjectType DATE_TYPE; protected JSType ERROR_FUNCTION_TYPE; protected ObjectType ERROR_TYPE; protected JSType EVAL_ERROR_FUNCTION_TYPE; protected ObjectType EVAL_ERROR_TYPE; protected FunctionType FUNCTION_FUNCTION_TYPE; protected FunctionType FUNCTION_INSTANCE_TYPE; protected ObjectType FUNCTION_PROTOTYPE; protected JSType GREATEST_FUNCTION_TYPE; protected JSType LEAST_FUNCTION_TYPE; protected JSType MATH_TYPE; protected JSType NULL_TYPE; protected JSType NUMBER_OBJECT_FUNCTION_TYPE; protected ObjectType NUMBER_OBJECT_TYPE; protected JSType NUMBER_STRING_BOOLEAN; protected JSType NUMBER_TYPE; protected FunctionType OBJECT_FUNCTION_TYPE; protected JSType NULL_VOID; protected JSType OBJECT_NUMBER_STRING; protected JSType OBJECT_NUMBER_STRING_BOOLEAN; protected JSType OBJECT_PROTOTYPE; protected ObjectType OBJECT_TYPE; protected JSType RANGE_ERROR_FUNCTION_TYPE; protected ObjectType RANGE_ERROR_TYPE; protected JSType REFERENCE_ERROR_FUNCTION_TYPE; protected ObjectType REFERENCE_ERROR_TYPE; protected JSType REGEXP_FUNCTION_TYPE; protected ObjectType REGEXP_TYPE; protected JSType STRING_OBJECT_FUNCTION_TYPE; protected ObjectType STRING_OBJECT_TYPE; protected JSType STRING_TYPE; protected JSType SYNTAX_ERROR_FUNCTION_

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>TYPE; protected ObjectType SYNTAX_ERROR_TYPE; protected JSType TYPE_ERROR_FUNCTION_TYPE; protected ObjectType TYPE_ERROR_TYPE; protected FunctionType U2U_CONSTRUCTOR_TYPE; protected FunctionType U2U_FUNCTION_TYPE; protected ObjectType UNKNOWN_TYPE; protected JSType URI_ERROR_FUNCTION_TYPE; protected ObjectType URI_ERROR_TYPE; protected JSType VOID_TYPE; protected int NATIVE_PROPERTIES_COUNT; @Override protected void setUp() throws Exception { super.setUp(); errorReporter = new TestErrorReporter(null, null); registry = new JSTypeRegistry(errorReporter); initTypes(); } protected void initTypes() { ALL_TYPE = registry.getNativeType(JSTypeNative.ALL_TYPE); NO_OBJECT_TYPE = registry.getNativeObjectType(JSTypeNative.NO_OBJECT_TYPE); NO_TYPE = registry.getNativeObjectType(JSTypeNative.NO_TYPE); NO_RESOLVED_TYPE = registry.getNativeObjectType(JSTypeNative.NO_RESOLVED_TYPE); ARRAY_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.ARRAY_FUNCTION_TYPE); ARRAY_TYPE = registry.getNativeObjectType(JSTypeNative.ARRAY_TYPE); BOOLEAN_OBJECT_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.BOOLEAN_OBJECT_FUNCTION_TYPE); BOOLEAN_OBJECT_TYPE = registry.getNativeObjectType(JSTypeNative.BOOLEAN_OBJECT_TYPE); BOOLEAN_TYPE = registry.getNativeType(JSTypeNative.BOOLEAN_TYPE); CHECKED_UNKNOWN_TYPE = registry.getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE); DATE_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.DATE_FUNCTION_TYPE); DATE_TYPE = registry.getNativeObjectType(JSTypeNative.DATE_TYPE); ERROR_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.ERROR_FUNCTION_TYPE); ERROR_TYPE = registry.getNativeObjectType(JSTypeNative.ERROR_TYPE); EVAL_ERROR_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.EVAL_ERROR_FUNCTION_TYPE); EVAL

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>ERROR_TYPE); REGEXP_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.REGEXP_FUNCTION_TYPE); REGEXP_TYPE = registry.getNativeObjectType(JSTypeNative.REGEXP_TYPE); STRING_OBJECT_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.STRING_OBJECT_FUNCTION_TYPE); STRING_OBJECT_TYPE = registry.getNativeObjectType(JSTypeNative.STRING_OBJECT_TYPE); STRING_TYPE = registry.getNativeType(JSTypeNative.STRING_TYPE); SYNTAX_ERROR_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.SYNTAX_ERROR_FUNCTION_TYPE); SYNTAX_ERROR_TYPE = registry.getNativeObjectType(JSTypeNative.SYNTAX_ERROR_TYPE); TYPE_ERROR_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.TYPE_ERROR_FUNCTION_TYPE); TYPE_ERROR_TYPE = registry.getNativeObjectType(JSTypeNative.TYPE_ERROR_TYPE); U2U_CONSTRUCTOR_TYPE = registry.getNativeFunctionType(JSTypeNative.U2U_CONSTRUCTOR_TYPE); U2U_FUNCTION_TYPE = registry.getNativeFunctionType(JSTypeNative.U2U_FUNCTION_TYPE); UNKNOWN_TYPE = registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE); URI_ERROR_FUNCTION_TYPE = registry.getNativeType(JSTypeNative.URI_ERROR_FUNCTION_TYPE); URI_ERROR_TYPE = registry.getNativeObjectType(JSTypeNative.URI_ERROR_TYPE); VOID_TYPE = registry.getNativeType(JSTypeNative.VOID_TYPE); addNativeProperties(registry); NATIVE_PROPERTIES_COUNT = OBJECT_TYPE.getPropertiesCount(); } /** Adds a basic set of properties to the native types. */ public static void addNativeProperties(JSTypeRegistry registry) { JSType booleanType = registry.getNativeType(JSTypeNative.BOOLEAN_TYPE); JSType numberType = registry.getNativeType(JSTypeNative.NUMBER_TYPE); JSType stringType = registry.getNativeType(JSTypeNative.STRING_TYPE); JSType unknownType = registry.getNativeType(JSTypeNative

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>.UNKNOWN_TYPE); ObjectType objectType = registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE); ObjectType arrayType = registry.getNativeObjectType(JSTypeNative.ARRAY_TYPE); ObjectType dateType = registry.getNativeObjectType(JSTypeNative.DATE_TYPE); ObjectType regexpType = registry.getNativeObjectType(JSTypeNative.REGEXP_TYPE); ObjectType booleanObjectType = registry.getNativeObjectType(JSTypeNative.BOOLEAN_OBJECT_TYPE); ObjectType numberObjectType = registry.getNativeObjectType(JSTypeNative.NUMBER_OBJECT_TYPE); ObjectType stringObjectType = registry.getNativeObjectType(JSTypeNative.STRING_OBJECT_TYPE); ObjectType objectPrototype = registry .getNativeFunctionType(JSTypeNative.OBJECT_FUNCTION_TYPE) .getPrototype(); addMethod(registry, objectPrototype, "constructor", objectType); addMethod(registry, objectPrototype, "toString", stringType); addMethod(registry, objectPrototype, "toLocaleString", stringType); addMethod(registry, objectPrototype, "valueOf", unknownType); addMethod(registry, objectPrototype, "hasOwnProperty", booleanType); addMethod(registry, objectPrototype, "isPrototypeOf", booleanType); addMethod(registry, objectPrototype, "propertyIsEnumerable", booleanType); ObjectType arrayPrototype = registry .getNativeFunctionType(JSTypeNative.ARRAY_FUNCTION_TYPE) .getPrototype(); addMethod(registry, arrayPrototype, "constructor", arrayType); addMethod(registry, arrayPrototype, "toString", stringType); addMethod(registry, arrayPrototype, "toLocaleString", stringType); addMethod(registry, arrayPrototype, "concat", arrayType); addMethod(registry, arrayPrototype, "join", stringType); addMethod(registry, arrayPrototype, "pop", unknownType); addMethod(registry, arrayPrototype, "push", numberType); addMethod(registry, arrayPrototype, "reverse", arrayType); addMethod(registry, arrayPrototype, "shift", unknownType); addMethod(registry, arrayPrototype, "slice", arrayType); addMethod(registry, arrayPrototype, "sort", arrayType); addMethod(registry, arrayPrototype, "splice", arrayType); addMethod(registry, arrayPrototype, "unshift", number

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>VOID: n.setJSType(getNativeType(VOID_TYPE)); break; case Token.STRING: n.setJSType(getNativeType(STRING_TYPE)); break; case Token.NUMBER: n.setJSType(getNativeType(NUMBER_TYPE)); break; case Token.TRUE: case Token.FALSE: n.setJSType(getNativeType(BOOLEAN_TYPE)); break; case Token.REGEXP: n.setJSType(getNativeType(REGEXP_TYPE)); break; case Token.OBJECTLIT: JSDocInfo info = n.getJSDocInfo(); if (info != null && info.getLendsName() != null) { if (lentObjectLiterals == null) { lentObjectLiterals = Lists.newArrayList(); } lentObjectLiterals.add(n); } else { defineObjectLiteral(n); } break; // NOTE(nicksantos): If we ever support Array tuples, // we will need to put ARRAYLIT here as well. } } private void defineObjectLiteral(Node objectLit) { // Handle the @lends annotation. JSType type = null; JSDocInfo info = objectLit.getJSDocInfo(); if (info != null && info.getLendsName() != null) { String lendsName = info.getLendsName(); Var lendsVar = scope.getVar(lendsName); if (lendsVar == null) { compiler.report( JSError.make(sourceName, objectLit, UNKNOWN_LENDS, lendsName)); } else { type = lendsVar.getType(); if (type == null) { type = typeRegistry.getNativeType(UNKNOWN_TYPE); } if (!type.isSubtype(typeRegistry.getNativeType(OBJECT_TYPE))) { compiler.report( JSError.make(sourceName, objectLit, LENDS_ON_NON_OBJECT, lendsName, type.toString())); type = null; } else { objectLit.setJSType(type); } } } info = NodeUtil.getBestJSDocInfo(object

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>(sourceName, n, MULTIPLE_VAR_DEF)); } for (Node name : n.children()) { defineName(name, n, parent, name.getJSDocInfo()); } } else { Node name = n.getFirstChild(); defineName(name, n, parent, (info != null) ? info : name.getJSDocInfo()); } } /** * Defines a function literal. */ void defineFunctionLiteral(Node n, Node parent) { assertDefinitionNode(n, Token.FUNCTION); // Determine the name and JSDocInfo and l-value for the function. // Any of these may be null. Node lValue = NodeUtil.getBestLValue(n); JSDocInfo info = NodeUtil.getBestJSDocInfo(n); String functionName = NodeUtil.getBestLValueName(lValue); FunctionType functionType = createFunctionTypeFromNodes(n, functionName, info, lValue); // Assigning the function type to the function node setDeferredType(n, functionType); // Declare this symbol in the current scope iff it's a function // declaration. Otherwise, the declaration will happen in other // code paths. if (NodeUtil.isFunctionDeclaration(n)) { defineSlot(n.getFirstChild(), n, functionType); } } /** * Defines a variable based on the {@link Token#NAME} node passed. * @param name The {@link Token#NAME} node. * @param var The parent of the {@code name} node, which must be a * {@link Token#VAR} node. * @param parent {@code var}'s parent. * @param info the {@link JSDocInfo} information relating to this * {@code name} node. */ private void defineName(Node name, Node var, Node parent, JSDocInfo info) { Node value = name.getFirstChild(); // variable's type JSType type = getDeclaredType(sourceName, info, name, value); if (type == null) { // The variable's type will be inferred. type = name.isFromExterns() ? getNativeType(UNKNOWN_TYPE) : null; }

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>(); String propName = n.getLastChild().getString(); String ownerName = stub.ownerName; boolean isExtern = stub.isExtern; if (scope.isDeclared(qName, false)) { continue; } // If we see a stub property, make sure to register this property // in the type registry. ObjectType ownerType = getObjectSlot(ownerName); ObjectType unknownType = typeRegistry.getNativeObjectType(UNKNOWN_TYPE); defineSlot(n, parent, unknownType, true); if (ownerType != null && (isExtern || ownerType.isFunctionPrototypeType())) { // If this is a stub for a prototype, just declare it // as an unknown type. These are seen often in externs. ownerType.defineInferredProperty( propName, unknownType, n); } else { typeRegistry.registerPropertyOnType( propName, ownerType == null ? unknownType : ownerType); } } } /** * Collects all declared properties in a function, and * resolves them relative to the global scope. */ private final class CollectProperties extends AbstractShallowStatementCallback { private final ObjectType thisType; CollectProperties(ObjectType thisType) { this.thisType = thisType; } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.isExprResult()) { Node child = n.getFirstChild(); switch (child.getType()) { case Token.ASSIGN: maybeCollectMember(t, child.getFirstChild(), child, child.getLastChild()); break; case Token.GETPROP: maybeCollectMember(t, child, child, null); break; } } } private void maybeCollectMember(NodeTraversal t, Node member, Node nodeWithJsDocInfo, @Nullable Node value) { JSDocInfo info = nodeWithJsDocInfo.getJSDocInfo(); // Do nothing if there is no JSDoc type info, or // if the node is not a member expression, or // if the member expression is not of the form: this.someProperty. if (info == null || !member.isGetProp() || !member.getFirstChild().isThis()) {

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> JSDocInfo info) { if (info == null || !info.hasTypedefType()) { return; } String typedef = candidate.getQualifiedName(); if (typedef == null) { return; } // TODO(nicksantos|user): This is a terrible, terrible hack // to bail out on recursive typedefs. We'll eventually need // to handle these properly. typeRegistry.declareType(typedef, getNativeType(UNKNOWN_TYPE)); JSType realType = info.getTypedefType().evaluate(scope, typeRegistry); if (realType == null) { compiler.report( JSError.make( t.getSourceName(), candidate, MALFORMED_TYPEDEF, typedef)); } typeRegistry.overwriteDeclaredType(typedef, realType); if (candidate.isGetProp()) { defineSlot(candidate, candidate.getParent(), getNativeType(NO_TYPE), false); } } } // end GlobalScopeBuilder /** * A shallow traversal of a local scope to find all arguments and * local variables. */ private final class LocalScopeBuilder extends AbstractScopeBuilder { /** * @param scope The scope that we're building. */ private LocalScopeBuilder(Scope scope) { super(scope); } /** * Traverse the scope root and build it. */ void build() { NodeTraversal.traverse(compiler, scope.getRootNode(), this); AstFunctionContents contents = getFunctionAnalysisResults(scope.getRootNode()); if (contents != null) { for (String varName : contents.getEscapedVarNames()) { Var v = scope.getVar(varName); Preconditions.checkState(v.getScope() == scope); v.markEscaped(); } } } /** * Visit a node in a local scope, and add any local variables or catch * parameters into the local symbol table. * * @param t The node traversal. * @param n The node being visited. * @param parent The parent of n */ @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n == scope.getRootNode()) return; if (n.isParamList() && parent == scope.getRootNode())

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> this function. It is only relevant for * constructors and may be {@code null}. */ private List<FunctionType> subTypes; /** * The template type name. May be {@code null}. */ private final ImmutableList<String> templateTypeNames; /** Creates an instance for a function that might be a constructor. */ FunctionType(JSTypeRegistry registry, String name, Node source, ArrowType arrowType, ObjectType typeOfThis, ImmutableList<String> templateTypeNames, boolean isConstructor, boolean nativeType) { super(registry, name, registry.getNativeObjectType(JSTypeNative.FUNCTION_INSTANCE_TYPE), nativeType); setPrettyPrint(true); Preconditions.checkArgument(source == null || Token.FUNCTION == source.getType()); Preconditions.checkNotNull(arrowType); this.source = source; this.kind = isConstructor ? Kind.CONSTRUCTOR : Kind.ORDINARY; if (isConstructor) { this.typeOfThis = typeOfThis != null ? typeOfThis : new InstanceObjectType(registry, this, nativeType); } else { this.typeOfThis = typeOfThis != null ? typeOfThis : registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE); } this.call = arrowType; this.templateTypeNames = templateTypeNames != null ? templateTypeNames : ImmutableList.<String>of(); } /** Creates an instance for a function that is an interface. */ private FunctionType(JSTypeRegistry registry, String name, Node source) { super(registry, name, registry.getNativeObjectType(JSTypeNative.FUNCTION_INSTANCE_TYPE)); setPrettyPrint(true); Preconditions.checkArgument(source == null || Token.FUNCTION == source.getType()); Preconditions.checkArgument(name != null); this.source = source; this.call = new ArrowType(registry, new Node(Token.PARAM_LIST), null); this.kind = Kind.INTERFACE; this.typeOfThis = new InstanceObjectType(registry, this); this.templateTypeNames = ImmutableList.of(); } /** Creates an instance for a function that is an interface. */ static FunctionType forInterface( JSTypeRegistry registry, String name, Node source) { return new FunctionType(registry, name

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS> } } return Integer.MAX_VALUE; } public JSType getReturnType() { return call.returnType; } public boolean isReturnTypeInferred() { return call.returnTypeInferred; } /** Gets the internal arrow type. For use by subclasses only. */ ArrowType getInternalArrowType() { return call; } @Override public Property getSlot(String name) { if ("prototype".equals(name)) { // Lazy initialization of the prototype field. getPrototype(); return prototypeSlot; } else { return super.getSlot(name); } } /** * Includes the prototype iff someone has created it. We do not want * to expose the prototype for ordinary functions. */ @Override public Set<String> getOwnPropertyNames() { if (prototypeSlot == null) { return super.getOwnPropertyNames(); } else { Set<String> names = Sets.newHashSet("prototype"); names.addAll(super.getOwnPropertyNames()); return names; } } /** * Gets the {@code prototype} property of this function type. This is * equivalent to {@code (ObjectType) getPropertyType("prototype")}. */ public ObjectType getPrototype() { // lazy initialization of the prototype field if (prototypeSlot == null) { String refName = getReferenceName(); if (refName == null) { // Someone is trying to access the prototype of a structural function. // We don't want to give real properties to this prototype, because // then it would propagate to all structural functions. setPrototypeNoCheck( registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE), null); } else { setPrototype( new PrototypeObjectType( registry, this.getReferenceName() + ".prototype", registry.getNativeObjectType(OBJECT_TYPE), isNativeObjectType()), null); } } return (ObjectType) prototypeSlot.getType(); } /** * Sets the prototype, creating the prototype object from the given * base type. * @param baseType The base type. */ public void setPrototypeBasedOn(ObjectType baseType) { setPrototypeBasedOn(baseType, null); } void setPrototypeBasedOn(ObjectType baseType, Node propertyNode)

Closure, 167

<FILEB>
<CHANGES>
left, leftType, leftIsRefineable? merged.typeA : null,
right, rightType, rightIsRefineable? merged.typeB : null);
<CHANGEE>
<CHANGES>
left, leftType, leftIsRefineable? restrictedLeftType : null,
right, rightType, rightIsRefineable? restrictedRightType : null);
<CHANGEE>
<CHANGES>
if (restrictedType!= null && restrictedType!= originalType) {
<CHANGEE>
<CHANGES>
Node left, JSType originalLeftType, JSType restrictedLeftType,
Node right, JSType originalRightType, JSType restrictedRightType) {
<CHANGEE>
<CHANGES>
restrictedLeftType!= null && restrictedLeftType!= originalLeftType;
<CHANGEE>
<CHANGES>
restrictedRightType!= null && restrictedRightType!= originalRightType;
<CHANGEE>
<CHANGES>
return maybeRestrictName(
blindScope, name, type,
type.getRestrictedTypeGivenToBooleanOutcome(outcome));
<CHANGEE>
<FILEE>
<FILEB>
<CHANGES>
if (outcome && this == getNativeType(JSTypeNative.UNKNOWN_TYPE)) {
return getNativeType(JSTypeNative.CHECKED_UNKNOWN_TYPE);
}
<CHANGEE>
<FILEE>
<FILEB> leftIsRefineable = false; leftType = left.getJSType(); } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); } // merged types TypePair merged = merging.apply(new TypePair(leftType, rightType)); // creating new scope if (merged != null) { return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, merged.typeA, right, rightIsRefineable, merged.typeB); <CHANGEE> } return blindScope; } private FlowScope caseAndOrNotShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { // left type JSType leftType = getTypeIfRefinable(left, blindScope); boolean leftIsRefineable; if (leftType != null) { leftIsRefineable = true; } else { leftIsRefineable = false; } // right type JSType rightType = getTypeIfRefinable(right, blindScope); boolean rightIsRefineable; if (rightType != null) { rightIsRefineable = true; } else { rightIsRefineable = false; rightType = right.getJSType(); blindScope = firstPreciserScopeKnowingConditionOutcome( right, blindScope, condition); } if (condition) { JSType restrictedRightType = (rightType == null) ? null : rightType.getRestrictedTypeGivenToBooleanOutcome(condition); // creating new scope return maybeRestrictTwoNames( blindScope, <CHANGES> left, leftIsRefineable, restrictedLeftType, right, rightIsRefineable, restrictedRightType); <CHANGEE> } return blindScope; } private FlowScope caseAndOrMaybeShortCircuiting(Node left, Node right, FlowScope blindScope, boolean condition) { FlowScope leftScope = firstPreciserScopeKnowingConditionOutcome( left, blindScope, !condition); Static<SCANS>variant of the other, // then we'll treat them as covariant (see comment above). other.typeOfThis.isSubtype(this.typeOfThis) || this.typeOfThis.isSubtype(other.typeOfThis); return treatThisTypesAsCovariant && this.call.isSubtype(other.call); } return getNativeType(JSTypeNative.FUNCTION_PROTOTYPE).isSubtype(that); } @Override public <T> T visit(Visitor<T> visitor) { return visitor.caseFunctionType(this); } /** * Gets the type of instance of this function. * @throws IllegalStateException if this function is not a constructor * (see {@link #isConstructor()}). */ public ObjectType getInstanceType() { Preconditions.checkState(hasInstanceType()); return typeOfThis; } /** * Sets the instance type. This should only be used for special * native types. */ void setInstanceType(ObjectType instanceType) { typeOfThis = instanceType; } /** * Returns whether this function type has an instance type. */ public boolean hasInstanceType() { return isConstructor() || isInterface(); } /** * Gets the type of {@code this} in this function. */ @Override public ObjectType getTypeOfThis() { return typeOfThis.isNoObjectType() ? registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE) : typeOfThis; } /** * Gets the source node or null if this is an unknown function. */ public Node getSource() { return source; } /** * Sets the source node. */ public void setSource(Node source) { if (prototypeSlot != null) { // NOTE(bashir): On one hand when source is null we want to drop any // references to old nodes retained in prototypeSlot. On the other hand // we cannot simply drop prototypeSlot, so we retain all information // except the propertyNode for which we use an approximation! These // details mostly matter in hot-swap passes. if (source == null || prototypeSlot.getNode() == null) { prototypeSlot = new Property(prototypeSlot.getName(), prototypeSlot.getType(), prototypeSlot.isTypeInferred(), source); }